@oneuptime/common 14.0.0 → 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 (1270) 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/Index.ts +4 -0
  7. package/Models/DatabaseModels/KubernetesCluster.ts +429 -0
  8. package/Models/DatabaseModels/Monitor.ts +48 -0
  9. package/Models/DatabaseModels/Runner.ts +5 -4
  10. package/Models/DatabaseModels/RunnerJob.ts +66 -0
  11. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +10 -2
  12. package/Models/DatabaseModels/ServiceLevelObjectiveFeed.ts +2 -0
  13. package/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.ts +522 -0
  14. package/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.ts +38 -4
  15. package/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.ts +603 -0
  16. package/Models/DatabaseModels/WorkflowVariable.ts +479 -2
  17. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +79 -25
  18. package/Server/API/AIInvestigationAPI.ts +341 -0
  19. package/Server/API/AutoRemediationAPI.ts +184 -2
  20. package/Server/API/CommonAPI.ts +70 -0
  21. package/Server/API/KubernetesClusterAiAccessAPI.ts +793 -0
  22. package/Server/API/MicrosoftTeamsAPI.ts +770 -315
  23. package/Server/API/MonitorAPI.ts +140 -0
  24. package/Server/API/OnCallScheduleTimelineAPI.ts +5 -21
  25. package/Server/API/SlackAPI.ts +447 -99
  26. package/Server/API/StatusPageAPI.ts +146 -26
  27. package/Server/API/TelemetryAPI.ts +216 -5
  28. package/Server/API/UserTwoFactorBackupCodeAPI.ts +1 -0
  29. package/Server/API/WorkflowVariableAPI.ts +156 -0
  30. package/Server/Infrastructure/GlobalCache.ts +35 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.ts +248 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.ts +19 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.ts +97 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.ts +129 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.ts +25 -0
  36. package/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.ts +20 -0
  37. package/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.ts +38 -0
  38. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -0
  39. package/Server/Middleware/MasterAdminAuthorization.ts +28 -0
  40. package/Server/Middleware/TelemetryIngest.ts +47 -2
  41. package/Server/Middleware/UserAuthorization.ts +56 -2
  42. package/Server/Services/AIAgentService.ts +1 -0
  43. package/Server/Services/AccessTokenService.ts +121 -48
  44. package/Server/Services/AlertEpisodeMemberService.ts +9 -0
  45. package/Server/Services/AlertEpisodeService.ts +14 -1
  46. package/Server/Services/AlertService.ts +134 -22
  47. package/Server/Services/AutoRemediationRuleEngineService.ts +968 -39
  48. package/Server/Services/AutoRemediationRuleService.ts +118 -0
  49. package/Server/Services/BillingService.ts +1 -0
  50. package/Server/Services/DatabaseService.ts +10 -0
  51. package/Server/Services/InboundEmail/Providers/SendGridInboundProvider.ts +16 -22
  52. package/Server/Services/IncidentEpisodeMemberService.ts +9 -0
  53. package/Server/Services/IncidentEpisodePublicNoteService.ts +66 -0
  54. package/Server/Services/IncidentEpisodeRoleMemberService.ts +27 -0
  55. package/Server/Services/IncidentEpisodeService.ts +14 -1
  56. package/Server/Services/IncidentGroupingEngineService.ts +11 -0
  57. package/Server/Services/IncidentMemberService.ts +21 -0
  58. package/Server/Services/IncidentPublicNoteService.ts +63 -0
  59. package/Server/Services/IncidentService.ts +120 -20
  60. package/Server/Services/IncidentStateTimelineService.ts +15 -0
  61. package/Server/Services/IncidentTemplateService.ts +83 -3
  62. package/Server/Services/Index.ts +4 -0
  63. package/Server/Services/KubernetesClusterAiAccessService.ts +1943 -0
  64. package/Server/Services/KubernetesClusterService.ts +1570 -1
  65. package/Server/Services/LogAggregationService.ts +14 -6
  66. package/Server/Services/MonitorService.ts +654 -54
  67. package/Server/Services/MonitorStatusTimelineService.ts +639 -2
  68. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -0
  69. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -0
  70. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -0
  71. package/Server/Services/OnCallDutyPolicyScheduleService.ts +3 -0
  72. package/Server/Services/OnCallNotificationAlertingService.ts +36 -12
  73. package/Server/Services/OnCallSetupReminderService.ts +1 -0
  74. package/Server/Services/ProbeService.ts +7 -1
  75. package/Server/Services/ProjectService.ts +11 -1
  76. package/Server/Services/RunbookCredentialService.ts +26 -1
  77. package/Server/Services/RunbookSecretService.ts +36 -0
  78. package/Server/Services/RunnerJobService.ts +941 -23
  79. package/Server/Services/RunnerService.ts +439 -6
  80. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +66 -0
  81. package/Server/Services/ScheduledMaintenanceService.ts +124 -19
  82. package/Server/Services/ScheduledMaintenanceTemplateService.ts +82 -3
  83. package/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.ts +385 -0
  84. package/Server/Services/ServiceLevelObjectiveLabelRuleService.ts +51 -0
  85. package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +92 -16
  86. package/Server/Services/ServiceLevelObjectiveMonitorRuleService.ts +36 -1
  87. package/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.ts +521 -0
  88. package/Server/Services/ServiceLevelObjectiveOwnerRuleService.ts +51 -0
  89. package/Server/Services/ServiceLevelObjectiveService.ts +42 -1
  90. package/Server/Services/StatusPagePrivateUserService.ts +1 -0
  91. package/Server/Services/StatusPageService.ts +301 -74
  92. package/Server/Services/StatusPageSubscriberService.ts +4 -0
  93. package/Server/Services/TeamMemberService.ts +309 -7
  94. package/Server/Services/TraceAggregationService.ts +10 -6
  95. package/Server/Services/UserEmailService.ts +1 -0
  96. package/Server/Services/UserNotificationMethodAdminService.ts +1 -0
  97. package/Server/Services/UserNotificationRuleAdminService.ts +1 -0
  98. package/Server/Services/UserNotificationRuleService.ts +4 -0
  99. package/Server/Services/UserService.ts +122 -1
  100. package/Server/Services/WorkflowVariableService.ts +593 -8
  101. package/Server/Services/WorkspaceNotificationRuleService.ts +588 -8
  102. package/Server/Services/WorkspaceProjectAuthTokenService.ts +170 -1
  103. package/Server/Types/Database/QueryHelper.ts +22 -0
  104. package/Server/Types/Markdown.ts +502 -27
  105. package/Server/Types/Workflow/Components/Telegram/SendMessageToChat.ts +27 -1
  106. package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +182 -0
  107. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +525 -0
  108. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +854 -0
  109. package/Server/Utils/AI/CodeFix/CodeFixAgentAuth.ts +22 -2
  110. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1916 -307
  111. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +1601 -74
  112. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +139 -7
  113. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +58 -1
  114. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +54 -1
  115. package/Server/Utils/AI/Toolbox/AIActionTools.ts +14 -6
  116. package/Server/Utils/AI/Toolbox/ResourceTools.ts +4 -2
  117. package/Server/Utils/AI/Toolbox/SloTools.ts +1 -0
  118. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +1341 -150
  119. package/Server/Utils/AutoRemediation/RemediationVerifier.ts +375 -4
  120. package/Server/Utils/Database/AffectedResourceRelations.ts +127 -0
  121. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +240 -1
  122. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +1 -0
  123. package/Server/Utils/Monitor/AffectedResourceList.ts +31 -73
  124. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +1 -1
  125. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +1 -1
  126. package/Server/Utils/Monitor/MonitorAlert.ts +83 -9
  127. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +181 -136
  128. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +5 -1
  129. package/Server/Utils/Monitor/MonitorIncident.ts +104 -9
  130. package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +124 -7
  131. package/Server/Utils/Monitor/ProbeMonitorsNotification.ts +407 -0
  132. package/Server/Utils/Monitor/RootCauseList.ts +139 -0
  133. package/Server/Utils/OnCall/OnCallShiftReminderRunner.ts +1 -0
  134. package/Server/Utils/PushNotificationUtil.ts +45 -0
  135. package/Server/Utils/Rules/OwnerRuleAssignment.ts +40 -3
  136. package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +28 -0
  137. package/Server/Utils/Runbook/RunbookExecutePermission.ts +56 -6
  138. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +223 -33
  139. package/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.ts +559 -0
  140. package/Server/Utils/Slo/SloRulePatternValidator.ts +42 -0
  141. package/Server/Utils/StartServer.ts +4 -3
  142. package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +528 -0
  143. package/Server/Utils/TwoFactorBackupCodeNotification.ts +1 -0
  144. package/Server/Utils/UserPermission/UserPermission.ts +16 -0
  145. package/Server/Utils/Workflow/OAuth2TokenClient.ts +704 -0
  146. package/Server/Utils/Workflow/WorkflowVariableOAuthToken.ts +318 -0
  147. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +47 -7
  148. package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +57 -16
  149. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +193 -214
  150. package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +57 -13
  151. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.ts +54 -17
  152. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +179 -161
  153. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +281 -167
  154. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.ts +114 -0
  155. package/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.ts +895 -0
  156. package/Server/Utils/Workspace/Slack/Actions/ActionTypes.ts +7 -9
  157. package/Server/Utils/Workspace/Slack/Actions/Alert.ts +96 -227
  158. package/Server/Utils/Workspace/Slack/Actions/AlertEpisode.ts +93 -214
  159. package/Server/Utils/Workspace/Slack/Actions/Auth.ts +30 -3
  160. package/Server/Utils/Workspace/Slack/Actions/Authorization.ts +99 -0
  161. package/Server/Utils/Workspace/Slack/Actions/Incident.ts +117 -283
  162. package/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.ts +101 -196
  163. package/Server/Utils/Workspace/Slack/Actions/ReactionNote.ts +329 -0
  164. package/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.ts +111 -287
  165. package/Server/Utils/Workspace/Slack/Slack.ts +86 -17
  166. package/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
  167. package/Server/Utils/Workspace/Workspace.ts +171 -54
  168. package/Server/Utils/Workspace/WorkspaceActionAuthorization.ts +252 -0
  169. package/Server/Utils/Workspace/WorkspaceBase.ts +39 -0
  170. package/Server/Utils/Workspace/WorkspaceOAuthState.ts +280 -0
  171. package/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.ts +96 -0
  172. package/Server/Utils/Workspace/WorkspaceReactionNote.ts +620 -0
  173. package/Tests/App/Dashboard/AttributesJSONDashboardSurfaces.test.tsx +496 -0
  174. package/Tests/App/Dashboard/AutoRemediationRulesTableCommandRunners.test.tsx +152 -0
  175. package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +32 -0
  176. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +794 -0
  177. package/Tests/App/Dashboard/CreateOAuthWorkflowVariableModal.test.tsx +840 -0
  178. package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +653 -0
  179. package/Tests/App/Dashboard/DatabaseMonitorStepForm.test.tsx +197 -0
  180. package/Tests/App/Dashboard/DatabaseMonitorView.test.tsx +376 -0
  181. package/Tests/App/Dashboard/DisabledWarningRobustness.test.tsx +262 -0
  182. package/Tests/App/Dashboard/EventNotes.test.tsx +2080 -0
  183. package/Tests/App/Dashboard/EventNotesPages.test.tsx +486 -0
  184. package/Tests/App/Dashboard/EventNotesUtil.test.ts +933 -0
  185. package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +218 -0
  186. package/Tests/App/Dashboard/FeedFilterSortIntegration.test.tsx +1632 -0
  187. package/Tests/App/Dashboard/FeedOptionsLocales.test.ts +250 -0
  188. package/Tests/App/Dashboard/FeedOrdering.test.ts +279 -9
  189. package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +665 -0
  190. package/Tests/App/Dashboard/IncidentEpisodeFeedPublicNoteNotifyDefault.test.tsx +494 -0
  191. package/Tests/App/Dashboard/IncidentEpisodeOverviewNotifyDefault.test.tsx +706 -0
  192. package/Tests/App/Dashboard/IncidentFeedPublicNoteNotifyDefault.test.tsx +472 -0
  193. package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +858 -0
  194. package/Tests/App/Dashboard/IncomingEmailAddressSettings.test.tsx +567 -0
  195. package/Tests/App/Dashboard/InvestigationClusterToolFormat.test.ts +241 -0
  196. package/Tests/App/Dashboard/InvestigationEvidenceKubectl.test.tsx +322 -0
  197. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +60 -1
  198. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +606 -3
  199. package/Tests/App/Dashboard/InvestigationPanelClusterAccess.test.tsx +476 -0
  200. package/Tests/App/Dashboard/InvestigationRunDetailsKubectl.test.tsx +383 -0
  201. package/Tests/App/Dashboard/KubectlActivitySummary.test.tsx +366 -0
  202. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +2886 -0
  203. package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +2688 -0
  204. package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +915 -0
  205. package/Tests/App/Dashboard/LayerDateTimeFieldTimezoneName.test.tsx +66 -0
  206. package/Tests/App/Dashboard/LogMonitorPreview.test.tsx +174 -0
  207. package/Tests/App/Dashboard/MonitorFeedRefresh.test.tsx +279 -0
  208. package/Tests/App/Dashboard/MonitorMetricQueryConfig.test.ts +244 -0
  209. package/Tests/App/Dashboard/MonitorOpenWorkCard.test.tsx +381 -0
  210. package/Tests/App/Dashboard/MonitorOpenWorkHook.test.tsx +784 -0
  211. package/Tests/App/Dashboard/MonitorOverviewData.test.tsx +2199 -0
  212. package/Tests/App/Dashboard/MonitorOverviewHero.test.tsx +1169 -0
  213. package/Tests/App/Dashboard/MonitorOverviewInput.test.ts +1127 -0
  214. package/Tests/App/Dashboard/MonitorOverviewPage.test.tsx +2136 -0
  215. package/Tests/App/Dashboard/MonitorOverviewSelectReadable.test.ts +368 -0
  216. package/Tests/App/Dashboard/MonitorOverviewStatBar.test.tsx +526 -0
  217. package/Tests/App/Dashboard/MonitorOverviewSupportingCards.test.tsx +542 -0
  218. package/Tests/App/Dashboard/MonitorOwnersCombine.test.ts +299 -0
  219. package/Tests/App/Dashboard/MonitorProbesCard.test.tsx +602 -0
  220. package/Tests/App/Dashboard/MonitorResponseTimeCard.test.tsx +386 -0
  221. package/Tests/App/Dashboard/MonitorSetupAndConnectionCards.test.tsx +612 -0
  222. package/Tests/App/Dashboard/MonitorStatusChangesCard.test.tsx +257 -0
  223. package/Tests/App/Dashboard/MonitorSummaryTestMonitorButton.test.tsx +360 -1
  224. package/Tests/App/Dashboard/MonitorUptimeHistoryCard.test.tsx +835 -0
  225. package/Tests/App/Dashboard/MonitorUptimeSummaryHook.test.tsx +971 -0
  226. package/Tests/App/Dashboard/MonitorViewLayout.test.tsx +580 -0
  227. package/Tests/App/Dashboard/OnCallLayerRestrictionTimesPreserved.test.tsx +441 -0
  228. package/Tests/App/Dashboard/OnCallNavbar.test.tsx +214 -0
  229. package/Tests/App/Dashboard/OnCallSchedulePreviewCalendarDisplay.test.tsx +1389 -0
  230. package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +205 -1
  231. package/Tests/App/Dashboard/OnCallScheduleTimeInputsSettingsZone.test.tsx +198 -0
  232. package/Tests/App/Dashboard/ProductOrientation.test.tsx +646 -0
  233. package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +524 -0
  234. package/Tests/App/Dashboard/RemediationSuggestionCardApproval.test.tsx +702 -0
  235. package/Tests/App/Dashboard/RemoveUserFromProject.test.tsx +427 -0
  236. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +4 -0
  237. package/Tests/App/Dashboard/ResourceFeed.test.tsx +612 -2
  238. package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +30 -0
  239. package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +1202 -0
  240. package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +3 -1
  241. package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +166 -0
  242. package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +622 -0
  243. package/Tests/App/Dashboard/ScheduledMaintenanceChangeStateNotifyDefault.test.tsx +1491 -0
  244. package/Tests/App/Dashboard/ScheduledMaintenanceFeedPublicNoteNotifyDefault.test.tsx +489 -0
  245. package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +52 -1
  246. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewNotifyDefault.test.tsx +909 -0
  247. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +14 -0
  248. package/Tests/App/Dashboard/ScheduledMaintenanceWindowStatsTranslation.test.tsx +307 -0
  249. package/Tests/App/Dashboard/SecurityEventAttributeColumnPicker.test.tsx +542 -0
  250. package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +174 -0
  251. package/Tests/App/Dashboard/SecurityEventConnectionsEmptyState.test.tsx +973 -212
  252. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +491 -52
  253. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +301 -0
  254. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +158 -1
  255. package/Tests/App/Dashboard/SecurityEventProviderTile.test.tsx +869 -0
  256. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +959 -94
  257. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +2 -0
  258. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +274 -1
  259. package/Tests/App/Dashboard/SloFeed.test.tsx +106 -1
  260. package/Tests/App/Dashboard/SloMonitorRuleCriteria.test.tsx +566 -0
  261. package/Tests/App/Dashboard/SloNavigationWiring.test.tsx +6 -4
  262. package/Tests/App/Dashboard/SloNoticeBanner.test.tsx +186 -0
  263. package/Tests/App/Dashboard/SloOverviewKpiStrip.test.tsx +388 -44
  264. package/Tests/App/Dashboard/SloOverviewLayout.test.tsx +613 -0
  265. package/Tests/App/Dashboard/TeamMembershipRemoval.test.ts +900 -0
  266. package/Tests/App/Dashboard/TeamMembershipRemovalPages.test.tsx +460 -0
  267. package/Tests/App/Dashboard/TimezoneElements.test.tsx +321 -0
  268. package/Tests/App/Dashboard/TimezoneSelectButton.test.tsx +634 -0
  269. package/Tests/App/Dashboard/TraceExplorer.test.tsx +260 -0
  270. package/Tests/App/Dashboard/TraceMonitorPreview.test.tsx +182 -0
  271. package/Tests/App/Dashboard/UserTimezoneInit.test.tsx +544 -0
  272. package/Tests/App/Dashboard/WorkflowVariableModals.test.tsx +1630 -0
  273. package/Tests/App/Dashboard/WorkflowVariableRoutes.test.tsx +603 -0
  274. package/Tests/App/Dashboard/WorkflowVariableTokenStatus.test.tsx +589 -0
  275. package/Tests/App/Dashboard/WorkflowVariableUtil.test.ts +1543 -0
  276. package/Tests/App/Dashboard/WorkflowVariableView.test.tsx +3227 -0
  277. package/Tests/App/Dashboard/WorkflowVariablesTable.test.tsx +1277 -395
  278. package/Tests/App/StatusPage/StatusPageOverviewMonitorGroupUptime.test.tsx +714 -0
  279. package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.NewYork.test.tsx +1009 -0
  280. package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.test.tsx +1092 -0
  281. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +5 -0
  282. package/Tests/Models/DatabaseModels/KubernetesClusterAiAccessColumns.test.ts +520 -0
  283. package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +66 -2
  284. package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +0 -8
  285. package/Tests/Models/DatabaseModels/SloLabelAndOwnerRuleModels.test.ts +404 -0
  286. package/Tests/Models/DatabaseModels/SloOverhaulModels.test.ts +17 -1
  287. package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
  288. package/Tests/Models/DatabaseModels/WorkspaceProjectAuthTokenAccessControl.test.ts +236 -0
  289. package/Tests/Models/IncidentEpisodePublicNoteNotifyColumnContract.test.ts +103 -0
  290. package/Tests/Models/IncidentPublicNoteNotifyColumnContract.test.ts +96 -0
  291. package/Tests/Models/ScheduledMaintenancePublicNoteNotifyColumnContract.test.ts +111 -0
  292. package/Tests/Server/API/AIInvestigationAPI.test.ts +976 -0
  293. package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +836 -0
  294. package/Tests/Server/API/CommonAPIAssertCanCreateTable.test.ts +565 -0
  295. package/Tests/Server/API/CommonAPIAssertCanReadTable.test.ts +282 -0
  296. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +1744 -0
  297. package/Tests/Server/API/LogHistogramAPI.test.ts +371 -0
  298. package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +125 -30
  299. package/Tests/Server/API/MicrosoftTeamsOAuthState.test.ts +1121 -0
  300. package/Tests/Server/API/MonitorUptimeSummaryAPI.test.ts +685 -0
  301. package/Tests/Server/API/SessionReplayAPI.test.ts +602 -1
  302. package/Tests/Server/API/SlackChannelCacheAPI.test.ts +358 -0
  303. package/Tests/Server/API/SlackOAuthState.test.ts +688 -0
  304. package/Tests/Server/API/StatusPageOverviewMonitorGroupUptime.test.ts +527 -0
  305. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +2 -0
  306. package/Tests/Server/API/StatusPageUptimeAPI.test.ts +427 -0
  307. package/Tests/Server/API/UserProjectsAPI.test.ts +4 -0
  308. package/Tests/Server/API/WorkflowVariableAPI.test.ts +404 -0
  309. package/Tests/Server/API/WorkspaceOAuthTestHelpers.ts +289 -0
  310. package/Tests/Server/API/WorkspaceSendTestNotificationRoutes.test.ts +2064 -0
  311. package/Tests/Server/Infrastructure/GlobalCache.test.ts +86 -0
  312. package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
  313. package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +589 -0
  314. package/Tests/Server/Infrastructure/Postgres/AddOAuth2WorkflowVariablesMigration.test.ts +254 -0
  315. package/Tests/Server/Infrastructure/Postgres/AddSloLabelAndOwnerRulesMigration.test.ts +407 -0
  316. package/Tests/Server/Infrastructure/Postgres/AddSloMonitorRuleMonitorTypeMigration.test.ts +171 -0
  317. package/Tests/Server/Infrastructure/Postgres/SloProductOverhaulMigration.test.ts +88 -4
  318. package/Tests/Server/Middleware/BlockedUserMiddleware.test.ts +403 -0
  319. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +13 -0
  320. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +2 -0
  321. package/Tests/Server/Middleware/RemovedProjectMemberAuthorization.test.ts +469 -0
  322. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +77 -1
  323. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +211 -0
  324. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +358 -0
  325. package/Tests/Server/Middleware/UserAuthorization.test.ts +97 -0
  326. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +2 -0
  327. package/Tests/Server/Services/AccessTokenProjectUserGrant.test.ts +14 -2
  328. package/Tests/Server/Services/AccessTokenRemovedMemberAccess.test.ts +1044 -0
  329. package/Tests/Server/Services/AddOwnersIdempotency.test.ts +5 -0
  330. package/Tests/Server/Services/AlertEpisodeMemberCreatePermission.test.ts +139 -0
  331. package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +1361 -0
  332. package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +375 -0
  333. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +2384 -0
  334. package/Tests/Server/Services/DatabaseServiceRuleCriteriaValidation.test.ts +104 -0
  335. package/Tests/Server/Services/FindOwnersProjectMembership.test.ts +267 -0
  336. package/Tests/Server/Services/InboundEmail/SendGridInboundProviderAddress.test.ts +90 -0
  337. package/Tests/Server/Services/IncidentEpisodeMemberCreatePermission.test.ts +143 -0
  338. package/Tests/Server/Services/IncidentEpisodePublicNoteNotifyDefault.test.ts +805 -0
  339. package/Tests/Server/Services/IncidentPublicNoteNotifyDefault.test.ts +724 -0
  340. package/Tests/Server/Services/IncidentRoleProjectMembership.test.ts +233 -0
  341. package/Tests/Server/Services/KubernetesClusterAiAccessAgentRunnerAssignments.test.ts +372 -0
  342. package/Tests/Server/Services/KubernetesClusterAiAccessBindingGuard.test.ts +1876 -0
  343. package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +620 -0
  344. package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +421 -0
  345. package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +3662 -0
  346. package/Tests/Server/Services/KubernetesClusterAiAccessSubjectClusters.test.ts +405 -0
  347. package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +975 -0
  348. package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +497 -0
  349. package/Tests/Server/Services/KubernetesClusterServiceDeleteSettlesRemediation.test.ts +328 -0
  350. package/Tests/Server/Services/LogAggregationService.test.ts +99 -0
  351. package/Tests/Server/Services/MonitorServiceIncomingEmailCustomAddress.test.ts +447 -0
  352. package/Tests/Server/Services/MonitorServiceProbeFlagSync.test.ts +1334 -0
  353. package/Tests/Server/Services/MonitorServiceProbeStatusGrouping.test.ts +1640 -0
  354. package/Tests/Server/Services/MonitorServiceRefreshCurrentStatus.test.ts +225 -0
  355. package/Tests/Server/Services/MonitorStatusBridgeProbePath.test.ts +8 -8
  356. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +264 -1
  357. package/Tests/Server/Services/MonitorStatusTimelineMergedDowntime.test.ts +284 -0
  358. package/Tests/Server/Services/MonitorUptimeSummaryService.test.ts +1043 -0
  359. package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +5 -0
  360. package/Tests/Server/Services/OnCallUndeliverablePageOwnerMembership.test.ts +359 -0
  361. package/Tests/Server/Services/ProbeServiceDashboardLink.test.ts +191 -0
  362. package/Tests/Server/Services/ProjectOwnersProjectMembership.test.ts +221 -0
  363. package/Tests/Server/Services/RuleRun/IncidentAlertMaintenanceRuleEnginesRun.test.ts +5 -0
  364. package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +48 -1
  365. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupARun.test.ts +28 -0
  366. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +39 -1
  367. package/Tests/Server/Services/RunnerJobEnqueueAiCommandAgentRunner.test.ts +230 -0
  368. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +978 -0
  369. package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +866 -0
  370. package/Tests/Server/Services/RunnerJobSubmitResultRedaction.test.ts +322 -0
  371. package/Tests/Server/Services/RunnerJobSubmitResultUnstorableText.test.ts +357 -0
  372. package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +227 -0
  373. package/Tests/Server/Services/RunnerServiceAgentName.test.ts +581 -0
  374. package/Tests/Server/Services/ScheduledMaintenancePublicNoteNotifyDefault.test.ts +802 -0
  375. package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +53 -0
  376. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +1175 -0
  377. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleServices.test.ts +172 -0
  378. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +323 -0
  379. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleService.test.ts +115 -0
  380. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleTriggers.test.ts +21 -2
  381. package/Tests/Server/Services/ServiceLevelObjectiveMonitorTypePostgres.test.ts +480 -0
  382. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +174 -0
  383. package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +45 -6
  384. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +1176 -1
  385. package/Tests/Server/Services/TeamMemberBillingSeatSync.test.ts +9 -0
  386. package/Tests/Server/Services/TeamMemberOnCallCleanup.test.ts +18 -0
  387. package/Tests/Server/Services/TeamMemberProjectMembership.test.ts +255 -0
  388. package/Tests/Server/Services/TeamMemberRemovalRevokesProjectAccess.test.ts +600 -0
  389. package/Tests/Server/Services/TeamMemberResourceCleanup.test.ts +731 -0
  390. package/Tests/Server/Services/TeamMemberWorkspaceAccountCleanup.test.ts +164 -0
  391. package/Tests/Server/Services/TraceAggregationService.test.ts +64 -0
  392. package/Tests/Server/Services/UserServiceBlockedUser.test.ts +293 -0
  393. package/Tests/Server/Services/WorkflowVariableOAuthHooks.test.ts +1016 -0
  394. package/Tests/Server/Services/WorkflowVariableOAuthPostgres.test.ts +543 -0
  395. package/Tests/Server/Services/WorkflowVariableRenameUniqueness.test.ts +16 -5
  396. package/Tests/Server/Services/WorkspaceNotificationRuleNoteDelivery.test.ts +473 -0
  397. package/Tests/Server/Services/WorkspaceNotificationRuleSendTestNotification.test.ts +3690 -0
  398. package/Tests/Server/Services/WorkspaceProjectAuthTokenMiscDataSecrets.test.ts +374 -0
  399. package/Tests/Server/Services/WorkspaceProjectAuthTokenSlackChannelCache.test.ts +143 -0
  400. package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +745 -0
  401. package/Tests/Server/Types/Database/Permissions/WorkflowVariableOAuthColumnPermission.test.ts +250 -0
  402. package/Tests/Server/Types/Database/QueryHelperJsonContains.test.ts +96 -0
  403. package/Tests/Server/Types/Markdown.test.ts +1035 -1
  404. package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +49 -0
  405. package/Tests/Server/Types/Workflow/Components/TelegramSendMessageToChat.test.ts +331 -0
  406. package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +285 -0
  407. package/Tests/Server/Utils/AI/CodeFix/CodeFixAgentAuth.test.ts +159 -1
  408. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +752 -0
  409. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +1840 -0
  410. package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +1067 -0
  411. package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +355 -0
  412. package/Tests/Server/Utils/AI/PostIncidentStatusUpdateTool.test.ts +496 -0
  413. package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +305 -0
  414. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +1365 -0
  415. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +639 -0
  416. package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +1585 -0
  417. package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +1065 -0
  418. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +2138 -1
  419. package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +1035 -0
  420. package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +517 -0
  421. package/Tests/Server/Utils/AI/ResourceTools.test.ts +27 -1
  422. package/Tests/Server/Utils/AI/SloTools.test.ts +10 -0
  423. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +1371 -24
  424. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorRollbackRace.test.ts +962 -0
  425. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +518 -0
  426. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +939 -2
  427. package/Tests/Server/Utils/Database/ProjectScopedRelationReferences.test.ts +371 -0
  428. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +64 -0
  429. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +1 -1
  430. package/Tests/Server/Utils/Monitor/DatabaseMetricObservationRemediation.test.ts +102 -0
  431. package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +362 -0
  432. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +405 -0
  433. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +1156 -0
  434. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +106 -80
  435. package/Tests/Server/Utils/Monitor/MonitorEmailAddressRedaction.test.ts +255 -0
  436. package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +347 -0
  437. package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +203 -0
  438. package/Tests/Server/Utils/Monitor/ProbeMonitorsNotification.test.ts +1147 -0
  439. package/Tests/Server/Utils/Monitor/RootCauseList.test.ts +252 -0
  440. package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +79 -0
  441. package/Tests/Server/Utils/ResourceFeed/ResourceFeedUtil.test.ts +21 -1
  442. package/Tests/Server/Utils/Rules/OwnerRuleAssignment.test.ts +155 -1
  443. package/Tests/Server/Utils/Rules/RuleRun/RuleRunPermission.test.ts +154 -0
  444. package/Tests/Server/Utils/SSRFProtection.test.ts +111 -0
  445. package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +288 -0
  446. package/Tests/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.test.ts +304 -0
  447. package/Tests/Server/Utils/Slo/SloRulePatternValidator.test.ts +67 -0
  448. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +40 -3
  449. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +213 -0
  450. package/Tests/Server/Utils/Workflow/OAuth2TokenClient.test.ts +702 -0
  451. package/Tests/Server/Utils/Workflow/OAuth2TokenClientHttpIntegration.test.ts +507 -0
  452. package/Tests/Server/Utils/Workflow/WorkflowVariableOAuthToken.test.ts +831 -0
  453. package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +63 -1
  454. package/Tests/Server/Utils/Workspace/MicrosoftTeamsAppAccessToken.test.ts +256 -0
  455. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +60 -0
  456. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChatActionAuthorization.test.ts +442 -0
  457. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +44 -2
  458. package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +55 -0
  459. package/Tests/Server/Utils/Workspace/MicrosoftTeamsReactionNoteSync.test.ts +1522 -0
  460. package/Tests/Server/Utils/Workspace/MicrosoftTeamsServiceUrl.test.ts +130 -0
  461. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +4 -4
  462. package/Tests/Server/Utils/Workspace/Slack/SlackActionAuthorization.test.ts +691 -0
  463. package/Tests/Server/Utils/Workspace/Slack/SlackMessageByTimestamp.test.ts +253 -0
  464. package/Tests/Server/Utils/Workspace/Slack/SlackReactionNote.test.ts +574 -0
  465. package/Tests/Server/Utils/Workspace/WorkspaceActionAuthorization.test.ts +488 -0
  466. package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +861 -0
  467. package/Tests/Server/Utils/Workspace/WorkspaceMessageAuthor.test.ts +635 -0
  468. package/Tests/Server/Utils/Workspace/WorkspaceOAuthState.test.ts +548 -0
  469. package/Tests/Server/Utils/Workspace/WorkspaceReactionNote.test.ts +839 -0
  470. package/Tests/Server/Utils/Workspace/WorkspaceSendErrorMessage.test.ts +725 -0
  471. package/Tests/Types/API/Route.test.ts +56 -0
  472. package/Tests/Types/API/URLSchemeLikePath.test.ts +304 -0
  473. package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +283 -9
  474. package/Tests/Types/DateBrowserLocalWallClock.test.ts +280 -0
  475. package/Tests/Types/DateLocalShortMonthNameFallback.test.ts +389 -0
  476. package/Tests/Types/DateUserTimezone.test.ts +177 -4
  477. package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +306 -0
  478. package/Tests/Types/Kubernetes/KubernetesClusterAiAccess.test.ts +283 -0
  479. package/Tests/Types/Kubernetes/KubernetesRunnerPostureWriteScope.test.ts +68 -0
  480. package/Tests/Types/Monitor/DatabaseMonitor/SqlServerPlatform.test.ts +144 -0
  481. package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +160 -0
  482. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +53 -0
  483. package/Tests/Types/Runbook/RunbookStepType.test.ts +91 -16
  484. package/Tests/Types/Runner/RunnerLiveStatus.test.ts +14 -3
  485. package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +15 -0
  486. package/Tests/Types/StatusPage/PublicNoteSubscriberNotificationDefault.test.ts +1868 -0
  487. package/Tests/Types/Timezone.test.ts +5 -0
  488. package/Tests/Types/TimezoneAlias.test.ts +1034 -0
  489. package/Tests/Types/Workflow/JiraTemplateFixtures.ts +833 -0
  490. package/Tests/Types/Workflow/JiraTemplateScripts.test.ts +5414 -0
  491. package/Tests/Types/Workflow/JiraTemplateSimulation.test.ts +4803 -0
  492. package/Tests/Types/Workflow/JiraTemplates.test.ts +5429 -0
  493. package/Tests/Types/Workflow/Templates.test.ts +79 -4
  494. package/Tests/Types/Workflow/WorkflowVariableOAuth.test.ts +520 -0
  495. package/Tests/Types/Workspace/WorkspaceNoteReaction.test.ts +177 -0
  496. package/Tests/UI/Components/AttributesJSON/AttributesJSONView.test.tsx +390 -0
  497. package/Tests/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.test.tsx +566 -0
  498. package/Tests/UI/Components/AttributesJSON/JSONHighlight.test.ts +131 -0
  499. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +276 -1
  500. package/Tests/UI/Components/Calendar/Calendar.test.tsx +1611 -0
  501. package/Tests/UI/Components/Calendar/CalendarDayLayout.test.ts +1486 -0
  502. package/Tests/UI/Components/Calendar/CalendarDayLayoutDst.test.ts +632 -0
  503. package/Tests/UI/Components/Calendar/CalendarDst.test.tsx +388 -0
  504. package/Tests/UI/Components/Charts/ChartGroupPlotFloor.test.tsx +170 -0
  505. package/Tests/UI/Components/Charts/HistogramSelection.test.ts +282 -0
  506. package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +6 -0
  507. package/Tests/UI/Components/CustomTimeRangeModalTimezoneTranslation.test.tsx +285 -0
  508. package/Tests/UI/Components/Dropdown.test.tsx +999 -2
  509. package/Tests/UI/Components/FeedOptions.test.ts +1404 -0
  510. package/Tests/UI/Components/FeedOptionsButton.test.tsx +2603 -0
  511. package/Tests/UI/Components/Forms/FormFieldCheckboxTranslation.test.tsx +216 -0
  512. package/Tests/UI/Components/Forms/FormFieldTimezoneDescriptionTranslation.test.tsx +461 -0
  513. package/Tests/UI/Components/Forms/IncidentEpisodePublicNoteNotifyPayload.test.tsx +641 -0
  514. package/Tests/UI/Components/Forms/IncidentPublicNoteNotifyPayload.test.tsx +346 -0
  515. package/Tests/UI/Components/Forms/ScheduledMaintenancePublicNoteNotifyPayload.test.tsx +493 -0
  516. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +911 -2
  517. package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.NewYork.test.tsx +730 -0
  518. package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.Tokyo.test.tsx +506 -0
  519. package/Tests/UI/Components/HistogramClickToZoom.test.tsx +619 -0
  520. package/Tests/UI/Components/Icon.test.tsx +26 -0
  521. package/Tests/UI/Components/LogDetailsPanelAttributesJSON.test.tsx +248 -0
  522. package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +47 -1
  523. package/Tests/UI/Components/LogsViewerSmallScreens.test.tsx +620 -0
  524. package/Tests/UI/Components/ModelPageModelChange.test.tsx +242 -3
  525. package/Tests/UI/Components/ModelTable/BaseModelTableDeleteConfirmation.test.tsx +521 -0
  526. package/Tests/UI/Components/MonitorGraphs/Uptime.test.tsx +406 -2
  527. package/Tests/UI/Components/MonitorGraphs/UptimeBarDayModal.test.tsx +169 -2
  528. package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
  529. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +266 -0
  530. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerSmallScreens.test.tsx +439 -0
  531. package/Tests/UI/Components/TimezoneDropdownLegacyValues.test.tsx +704 -0
  532. package/Tests/UI/Components/UseHistogramZoom.test.tsx +89 -1
  533. package/Tests/UI/Components/UseLogsHistogram.test.tsx +175 -0
  534. package/Tests/UI/Components/useFeedItemsViewKey.test.tsx +1017 -0
  535. package/Tests/UI/Components/useFeedOptions.test.tsx +1107 -0
  536. package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +5 -5
  537. package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +28 -0
  538. package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +51 -0
  539. package/Tests/UI/Utils/Timezone.test.ts +747 -33
  540. package/Tests/UI/Workspace/SendTestNotificationButton.test.tsx +1854 -0
  541. package/Tests/UI/Workspace/WorkspaceCardsSendTestNotification.test.tsx +1864 -0
  542. package/Tests/Utils/AI/InvestigationReport.test.ts +435 -7
  543. package/Tests/Utils/AiRemediation/KubectlAllowlistValidatorParity.test.ts +1084 -0
  544. package/Tests/Utils/AiRemediation/KubectlOutputRedactor.test.ts +2531 -0
  545. package/Tests/Utils/AiRemediation/KubectlPolicy.test.ts +1895 -0
  546. package/Tests/Utils/AiRemediation/KubectlPolicyAllowlist.test.ts +428 -0
  547. package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +828 -0
  548. package/Tests/Utils/AiRemediation/KubectlPolicyCustomResourceScope.test.ts +573 -0
  549. package/Tests/Utils/AiRemediation/KubectlPolicyDenials.test.ts +1525 -0
  550. package/Tests/Utils/AiRemediation/KubectlPolicyExposeOverrides.test.ts +275 -0
  551. package/Tests/Utils/AiRemediation/KubectlPolicyNodePatch.test.ts +428 -0
  552. package/Tests/Utils/AiRemediation/KubectlPolicyObjectWords.test.ts +195 -0
  553. package/Tests/Utils/AiRemediation/KubectlPolicyPatchParents.test.ts +637 -0
  554. package/Tests/Utils/AiRemediation/KubectlPolicyPodSecurityBaseline.test.ts +322 -0
  555. package/Tests/Utils/AiRemediation/KubectlPolicySafeWriteScope.test.ts +1026 -0
  556. package/Tests/Utils/AiRemediation/KubectlWaitBudget.test.ts +267 -0
  557. package/Tests/Utils/AiRemediation/KubectlWriteScope.test.ts +390 -0
  558. package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +777 -0
  559. package/Tests/Utils/AiRemediation/KubectlWriteScopeClusterScoped.test.ts +613 -0
  560. package/Tests/Utils/AiRemediation/KubectlWriteScopeEntryPoint.test.ts +576 -0
  561. package/Tests/Utils/AiRemediation/KubectlWriteScopeKubectlSpellings.test.ts +752 -0
  562. package/Tests/Utils/AiRemediation/KubectlWriteScopeObjectWords.test.ts +358 -0
  563. package/Tests/Utils/AiRemediation/KubectlWriteScopeOverrides.test.ts +293 -0
  564. package/Tests/Utils/AiRemediation/KubectlWriteScopeParityCases.ts +598 -0
  565. package/Tests/Utils/AiRemediation/KubectlWriteScopePolicyParity.test.ts +403 -0
  566. package/Tests/Utils/Dashboard/DashboardClockComponent.test.ts +154 -5
  567. package/Tests/Utils/LabelRuleImportExport.test.ts +102 -0
  568. package/Tests/Utils/Monitor/IncomingEmailMonitorAddress.test.ts +478 -0
  569. package/Tests/Utils/Monitor/MonitorCheckScheduleUtil.test.ts +646 -0
  570. package/Tests/Utils/Monitor/MonitorOverviewCriteriaUtil.test.ts +292 -0
  571. package/Tests/Utils/Monitor/MonitorOverviewFamily.test.ts +274 -0
  572. package/Tests/Utils/Monitor/MonitorOverviewPresentationUtil.test.ts +3100 -0
  573. package/Tests/Utils/Monitor/MonitorOverviewProbeUtil.test.ts +1412 -0
  574. package/Tests/Utils/Monitor/MonitorOverviewTargetUtil.test.ts +374 -0
  575. package/Tests/Utils/Monitor/MonitorStatusHistoryUtil.test.ts +332 -0
  576. package/Tests/Utils/Monitor/MonitorUptimeSummaryUtil.test.ts +946 -0
  577. package/Tests/Utils/Rum/SessionIdentity.test.ts +94 -0
  578. package/Tests/Utils/Rum/SessionReplayWafCompatibility.test.ts +840 -0
  579. package/Tests/Utils/Rum/SessionReplayWireEncoding.test.ts +208 -0
  580. package/Tests/Utils/Rum/UserFlow.test.ts +1010 -0
  581. package/Tests/Utils/Slo/SloDuration.test.ts +115 -24
  582. package/Tests/Utils/Slo/SloHealth.test.ts +93 -10
  583. package/Tests/Utils/Slo/SloMonitorRuleCriteria.test.ts +109 -0
  584. package/Tests/Utils/Slo/SloOverviewText.test.ts +64 -0
  585. package/Tests/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.test.ts +217 -0
  586. package/Tests/Utils/StatusPage/ResourceUptime.test.ts +1272 -1
  587. package/Tests/Utils/StatusPage/UptimeDailyAggregateTotals.test.ts +298 -0
  588. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +649 -1
  589. package/Tests/Utils/StatusPage/UptimeDailyAggregateWindow.test.ts +314 -0
  590. package/Tests/Utils/Telemetry/AttributesJSON.test.ts +454 -0
  591. package/Tests/Utils/Uptime/DayUptimeGraphUtil.test.ts +418 -2
  592. package/Tests/Utils/Uptime/DayUptimeGraphUtilMidnightDst.test.ts +160 -0
  593. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Auckland.test.ts +45 -0
  594. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Kolkata.test.ts +40 -0
  595. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.London.test.ts +147 -0
  596. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.NewYork.test.ts +48 -0
  597. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Tokyo.test.ts +39 -0
  598. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezoneCases.ts +1015 -0
  599. package/Tests/Utils/Uptime/UptimeUtil.test.ts +75 -0
  600. package/Tests/Utils/Uptime/UptimeUtilChronologicalOrder.test.ts +1279 -0
  601. package/Types/API/Route.ts +19 -2
  602. package/Types/API/URL.ts +26 -2
  603. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +203 -5
  604. package/Types/Date.ts +121 -7
  605. package/Types/Email/EmailMessage.ts +8 -0
  606. package/Types/Email/EmailTemplateType.ts +1 -0
  607. package/Types/Exception/ExceptionMessages.ts +1 -0
  608. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +488 -0
  609. package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +54 -0
  610. package/Types/Kubernetes/KubernetesClusterAiAccessToolNames.ts +17 -0
  611. package/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.ts +9 -0
  612. package/Types/Monitor/DatabaseMonitor/SqlServerPlatform.ts +117 -0
  613. package/Types/Monitor/KubernetesAlertTemplates.ts +6 -5
  614. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +6 -5
  615. package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +9 -0
  616. package/Types/Monitor/MonitorStepExceptionMonitor.ts +56 -0
  617. package/Types/Monitor/MonitorUptimeSummary.ts +113 -0
  618. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +11 -0
  619. package/Types/Permission.ts +90 -0
  620. package/Types/Rules/RuleCriteriaFieldRegistry.ts +11 -0
  621. package/Types/Rules/RuleRun.ts +4 -0
  622. package/Types/Rum/SessionReplay.ts +55 -2
  623. package/Types/Rum/SessionReplayApi.ts +44 -1
  624. package/Types/Rum/UserFlow.ts +187 -0
  625. package/Types/Runbook/RunbookStepType.ts +10 -0
  626. package/Types/Runbook/RunnerJobOrigin.ts +15 -0
  627. package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +6 -2
  628. package/Types/StatusPage/MergedDowntimeTotals.ts +17 -0
  629. package/Types/StatusPage/PublicNoteSubscriberNotificationDefault.ts +148 -0
  630. package/Types/StatusPage/UptimeDailyAggregate.ts +15 -0
  631. package/Types/Telemetry/TelemetryIngestSurface.ts +9 -0
  632. package/Types/Timezone.ts +5 -0
  633. package/Types/TimezoneAlias.ts +385 -0
  634. package/Types/Workflow/Templates.ts +3041 -2
  635. package/Types/Workflow/WorkflowVariableOAuth.ts +476 -0
  636. package/Types/Workspace/WorkspaceNoteReaction.ts +132 -0
  637. package/Typings/Index.d.ts +24 -0
  638. package/UI/Components/AttributesJSON/AttributesJSONPreferences.ts +175 -0
  639. package/UI/Components/AttributesJSON/AttributesJSONView.tsx +138 -0
  640. package/UI/Components/AttributesJSON/AttributesViewToggle.tsx +79 -0
  641. package/UI/Components/AttributesJSON/BracesGlyph.tsx +30 -0
  642. package/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.tsx +491 -0
  643. package/UI/Components/AttributesJSON/JSONHighlight.ts +77 -0
  644. package/UI/Components/Calendar/Calendar.css +83 -0
  645. package/UI/Components/Calendar/Calendar.tsx +136 -7
  646. package/UI/Components/Calendar/CalendarDayLayout.ts +236 -0
  647. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +23 -1
  648. package/UI/Components/Charts/Utils/HistogramSelection.ts +117 -0
  649. package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +269 -0
  650. package/UI/Components/Charts/Utils/useHistogramZoom.ts +26 -2
  651. package/UI/Components/Date/CustomTimeRangeModal.tsx +26 -3
  652. package/UI/Components/Dropdown/Dropdown.tsx +58 -9
  653. package/UI/Components/Feed/FeedOptions.ts +427 -0
  654. package/UI/Components/Feed/FeedOptionsButton.tsx +692 -0
  655. package/UI/Components/Feed/useFeedItems.ts +28 -9
  656. package/UI/Components/Feed/useFeedOptions.ts +225 -0
  657. package/UI/Components/Forms/Fields/FormField.tsx +49 -14
  658. package/UI/Components/Graphs/DayUptimeGraph.tsx +166 -25
  659. package/UI/Components/Graphs/UptimeBarTooltip.tsx +10 -2
  660. package/UI/Components/Graphs/UptimeDaySummary.tsx +8 -0
  661. package/UI/Components/Icon/Icon.tsx +1 -1
  662. package/UI/Components/LogsViewer/LogsViewer.tsx +52 -5
  663. package/UI/Components/LogsViewer/components/ColumnSelector.tsx +13 -2
  664. package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +13 -1
  665. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +91 -76
  666. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +15 -1
  667. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +28 -106
  668. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +54 -6
  669. package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +8 -2
  670. package/UI/Components/LogsViewer/useLogsHistogram.ts +39 -6
  671. package/UI/Components/ModelTable/BaseModelTable.tsx +66 -14
  672. package/UI/Components/MonitorGraphs/Uptime.tsx +12 -0
  673. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +10 -2
  674. package/UI/Components/Page/ModelPage.tsx +59 -3
  675. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +69 -5
  676. package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +12 -1
  677. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +15 -1
  678. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +36 -104
  679. package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +9 -1
  680. package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +19 -1
  681. package/UI/Utils/Timezone.ts +69 -28
  682. package/Utils/AI/InvestigationReport.ts +195 -25
  683. package/Utils/AiRemediation/KubectlOutputRedactor.ts +1724 -0
  684. package/Utils/AiRemediation/KubectlPolicy.ts +4136 -0
  685. package/Utils/AiRemediation/KubectlWaitBudget.ts +156 -0
  686. package/Utils/AiRemediation/KubectlWriteScope.ts +1964 -0
  687. package/Utils/Dashboard/Components/DashboardClockComponent.ts +7 -6
  688. package/Utils/LabelRuleImportExport.ts +27 -9
  689. package/Utils/Monitor/IncomingEmailMonitorAddress.ts +292 -0
  690. package/Utils/Monitor/MetricValueFormatter.ts +2 -2
  691. package/Utils/Monitor/MonitorCheckScheduleUtil.ts +654 -0
  692. package/Utils/Monitor/MonitorOverviewCriteriaUtil.ts +188 -0
  693. package/Utils/Monitor/MonitorOverviewFamily.ts +263 -0
  694. package/Utils/Monitor/MonitorOverviewPresentationUtil.ts +1958 -0
  695. package/Utils/Monitor/MonitorOverviewProbeUtil.ts +979 -0
  696. package/Utils/Monitor/MonitorOverviewTargetUtil.ts +299 -0
  697. package/Utils/Monitor/MonitorStatusHistoryUtil.ts +201 -0
  698. package/Utils/Monitor/MonitorUptimeSummaryUtil.ts +638 -0
  699. package/Utils/Rules/MonitorTypeRuleCriteria.ts +31 -0
  700. package/Utils/Rum/SessionIdentity.ts +44 -0
  701. package/Utils/Rum/SessionReplayWireEncoding.ts +137 -0
  702. package/Utils/Rum/UserFlow.ts +1527 -0
  703. package/Utils/Slo/SloDuration.ts +22 -6
  704. package/Utils/Slo/SloHealth.ts +8 -7
  705. package/Utils/Slo/SloMonitorRuleCriteria.ts +33 -1
  706. package/Utils/Slo/SloOverviewText.ts +33 -0
  707. package/Utils/Slo/SloProjection.ts +4 -3
  708. package/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.ts +107 -0
  709. package/Utils/StatusPage/ResourceUptime.ts +99 -0
  710. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +197 -0
  711. package/Utils/Telemetry/AttributesJSON.ts +367 -0
  712. package/Utils/Uptime/DayUptimeGraphUtil.ts +165 -0
  713. package/Utils/Uptime/UptimeUtil.ts +122 -38
  714. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +8 -1
  715. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
  716. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
  717. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
  718. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +8 -1
  719. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
  720. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +10 -3
  721. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  722. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +10 -3
  723. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  724. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  725. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  726. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +434 -0
  727. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  728. package/build/dist/Models/DatabaseModels/Monitor.js +49 -0
  729. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  730. package/build/dist/Models/DatabaseModels/Runner.js +4 -4
  731. package/build/dist/Models/DatabaseModels/Runner.js.map +1 -1
  732. package/build/dist/Models/DatabaseModels/RunnerJob.js +66 -0
  733. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  734. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +11 -3
  735. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  736. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js +2 -0
  737. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js.map +1 -1
  738. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js +530 -0
  739. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js.map +1 -0
  740. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js +39 -4
  741. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js.map +1 -1
  742. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js +612 -0
  743. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js.map +1 -0
  744. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +487 -3
  745. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  746. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js +64 -16
  747. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  748. package/build/dist/Server/API/AIInvestigationAPI.js +205 -0
  749. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  750. package/build/dist/Server/API/AutoRemediationAPI.js +115 -2
  751. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  752. package/build/dist/Server/API/CommonAPI.js +49 -0
  753. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  754. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +564 -0
  755. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -0
  756. package/build/dist/Server/API/MicrosoftTeamsAPI.js +465 -176
  757. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  758. package/build/dist/Server/API/MonitorAPI.js +107 -1
  759. package/build/dist/Server/API/MonitorAPI.js.map +1 -1
  760. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +5 -16
  761. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -1
  762. package/build/dist/Server/API/SlackAPI.js +322 -78
  763. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  764. package/build/dist/Server/API/StatusPageAPI.js +123 -17
  765. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  766. package/build/dist/Server/API/TelemetryAPI.js +144 -5
  767. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  768. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js +1 -0
  769. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js.map +1 -1
  770. package/build/dist/Server/API/WorkflowVariableAPI.js +109 -0
  771. package/build/dist/Server/API/WorkflowVariableAPI.js.map +1 -0
  772. package/build/dist/Server/Infrastructure/GlobalCache.js +29 -0
  773. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  774. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js +93 -0
  775. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js.map +1 -0
  776. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js +12 -0
  777. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js.map +1 -0
  778. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js +38 -0
  779. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js.map +1 -0
  780. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js +54 -0
  781. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js.map +1 -0
  782. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js +14 -0
  783. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js.map +1 -0
  784. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js +12 -0
  785. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js.map +1 -0
  786. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js +32 -0
  787. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js.map +1 -0
  788. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -0
  789. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  790. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +21 -0
  791. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  792. package/build/dist/Server/Middleware/TelemetryIngest.js +32 -2
  793. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  794. package/build/dist/Server/Middleware/UserAuthorization.js +39 -6
  795. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  796. package/build/dist/Server/Services/AIAgentService.js +1 -0
  797. package/build/dist/Server/Services/AIAgentService.js.map +1 -1
  798. package/build/dist/Server/Services/AccessTokenService.js +87 -42
  799. package/build/dist/Server/Services/AccessTokenService.js.map +1 -1
  800. package/build/dist/Server/Services/AlertEpisodeMemberService.js +4 -0
  801. package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -1
  802. package/build/dist/Server/Services/AlertEpisodeService.js +12 -1
  803. package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
  804. package/build/dist/Server/Services/AlertService.js +110 -23
  805. package/build/dist/Server/Services/AlertService.js.map +1 -1
  806. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +716 -30
  807. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  808. package/build/dist/Server/Services/AutoRemediationRuleService.js +100 -0
  809. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
  810. package/build/dist/Server/Services/BillingService.js +1 -0
  811. package/build/dist/Server/Services/BillingService.js.map +1 -1
  812. package/build/dist/Server/Services/DatabaseService.js +7 -0
  813. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  814. package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js +11 -16
  815. package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js.map +1 -1
  816. package/build/dist/Server/Services/IncidentEpisodeMemberService.js +4 -0
  817. package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
  818. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +48 -0
  819. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  820. package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js +21 -0
  821. package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js.map +1 -1
  822. package/build/dist/Server/Services/IncidentEpisodeService.js +12 -1
  823. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  824. package/build/dist/Server/Services/IncidentGroupingEngineService.js +8 -0
  825. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  826. package/build/dist/Server/Services/IncidentMemberService.js +14 -0
  827. package/build/dist/Server/Services/IncidentMemberService.js.map +1 -1
  828. package/build/dist/Server/Services/IncidentPublicNoteService.js +48 -0
  829. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  830. package/build/dist/Server/Services/IncidentService.js +99 -21
  831. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  832. package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
  833. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  834. package/build/dist/Server/Services/IncidentTemplateService.js +68 -4
  835. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  836. package/build/dist/Server/Services/Index.js +4 -0
  837. package/build/dist/Server/Services/Index.js.map +1 -1
  838. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +1470 -0
  839. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -0
  840. package/build/dist/Server/Services/KubernetesClusterService.js +1097 -0
  841. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  842. package/build/dist/Server/Services/LogAggregationService.js +14 -6
  843. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  844. package/build/dist/Server/Services/MonitorService.js +490 -50
  845. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  846. package/build/dist/Server/Services/MonitorStatusTimelineService.js +540 -3
  847. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  848. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -0
  849. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  850. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -0
  851. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  852. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -0
  853. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  854. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +3 -0
  855. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  856. package/build/dist/Server/Services/OnCallNotificationAlertingService.js +29 -7
  857. package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -1
  858. package/build/dist/Server/Services/OnCallSetupReminderService.js +1 -0
  859. package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -1
  860. package/build/dist/Server/Services/ProbeService.js +7 -1
  861. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  862. package/build/dist/Server/Services/ProjectService.js +11 -1
  863. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  864. package/build/dist/Server/Services/RunbookCredentialService.js +24 -0
  865. package/build/dist/Server/Services/RunbookCredentialService.js.map +1 -1
  866. package/build/dist/Server/Services/RunbookSecretService.js +44 -0
  867. package/build/dist/Server/Services/RunbookSecretService.js.map +1 -1
  868. package/build/dist/Server/Services/RunnerJobService.js +641 -21
  869. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  870. package/build/dist/Server/Services/RunnerService.js +344 -5
  871. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  872. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +48 -0
  873. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  874. package/build/dist/Server/Services/ScheduledMaintenanceService.js +102 -20
  875. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  876. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +67 -4
  877. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
  878. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js +295 -0
  879. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js.map +1 -0
  880. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js +56 -0
  881. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js.map +1 -0
  882. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +59 -14
  883. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -1
  884. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js +22 -1
  885. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js.map +1 -1
  886. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js +400 -0
  887. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js.map +1 -0
  888. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js +56 -0
  889. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js.map +1 -0
  890. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +33 -1
  891. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  892. package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -0
  893. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  894. package/build/dist/Server/Services/StatusPageService.js +230 -32
  895. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  896. package/build/dist/Server/Services/StatusPageSubscriberService.js +4 -0
  897. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  898. package/build/dist/Server/Services/TeamMemberService.js +270 -11
  899. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  900. package/build/dist/Server/Services/TraceAggregationService.js +10 -6
  901. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  902. package/build/dist/Server/Services/UserEmailService.js +1 -0
  903. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  904. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1 -0
  905. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
  906. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +1 -0
  907. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
  908. package/build/dist/Server/Services/UserNotificationRuleService.js +4 -0
  909. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  910. package/build/dist/Server/Services/UserService.js +107 -1
  911. package/build/dist/Server/Services/UserService.js.map +1 -1
  912. package/build/dist/Server/Services/WorkflowVariableService.js +396 -7
  913. package/build/dist/Server/Services/WorkflowVariableService.js.map +1 -1
  914. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +463 -7
  915. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  916. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +144 -2
  917. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  918. package/build/dist/Server/Types/Database/QueryHelper.js +20 -0
  919. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  920. package/build/dist/Server/Types/Markdown.js +394 -27
  921. package/build/dist/Server/Types/Markdown.js.map +1 -1
  922. package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js +20 -1
  923. package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js.map +1 -1
  924. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +123 -0
  925. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -0
  926. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +344 -0
  927. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -0
  928. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +587 -0
  929. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -0
  930. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js +18 -2
  931. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js.map +1 -1
  932. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1380 -239
  933. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  934. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +1099 -68
  935. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  936. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +88 -6
  937. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  938. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +45 -11
  939. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  940. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +41 -11
  941. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  942. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +11 -4
  943. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
  944. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +4 -2
  945. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
  946. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +1 -0
  947. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -1
  948. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +997 -141
  949. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  950. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +299 -4
  951. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
  952. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +120 -0
  953. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -0
  954. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +160 -1
  955. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  956. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +1 -0
  957. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
  958. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +22 -51
  959. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -1
  960. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +1 -1
  961. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +1 -1
  962. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +59 -12
  963. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  964. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +139 -109
  965. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  966. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +5 -1
  967. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  968. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +84 -22
  969. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  970. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +70 -5
  971. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -1
  972. package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js +249 -0
  973. package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js.map +1 -0
  974. package/build/dist/Server/Utils/Monitor/RootCauseList.js +101 -0
  975. package/build/dist/Server/Utils/Monitor/RootCauseList.js.map +1 -0
  976. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js +1 -0
  977. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js.map +1 -1
  978. package/build/dist/Server/Utils/PushNotificationUtil.js +27 -0
  979. package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
  980. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js +26 -3
  981. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js.map +1 -1
  982. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +28 -0
  983. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
  984. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js +38 -3
  985. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js.map +1 -1
  986. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +143 -23
  987. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  988. package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js +406 -0
  989. package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js.map +1 -0
  990. package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js +28 -0
  991. package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js.map +1 -0
  992. package/build/dist/Server/Utils/StartServer.js +4 -3
  993. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  994. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +382 -0
  995. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -0
  996. package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js +1 -0
  997. package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js.map +1 -1
  998. package/build/dist/Server/Utils/UserPermission/UserPermission.js +10 -0
  999. package/build/dist/Server/Utils/UserPermission/UserPermission.js.map +1 -1
  1000. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +447 -0
  1001. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -0
  1002. package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js +202 -0
  1003. package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js.map +1 -0
  1004. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +39 -1
  1005. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  1006. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +44 -4
  1007. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
  1008. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +150 -191
  1009. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  1010. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +44 -4
  1011. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
  1012. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js +39 -17
  1013. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js.map +1 -1
  1014. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +151 -145
  1015. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
  1016. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +221 -127
  1017. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  1018. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js +91 -0
  1019. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js.map +1 -0
  1020. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js +673 -0
  1021. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js.map +1 -0
  1022. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js +3 -9
  1023. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js.map +1 -1
  1024. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +65 -166
  1025. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
  1026. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js +63 -157
  1027. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js.map +1 -1
  1028. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js +23 -3
  1029. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js.map +1 -1
  1030. package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js +92 -0
  1031. package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js.map +1 -0
  1032. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +81 -210
  1033. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
  1034. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js +68 -138
  1035. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js.map +1 -1
  1036. package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js +240 -0
  1037. package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js.map +1 -0
  1038. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js +85 -212
  1039. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js.map +1 -1
  1040. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +60 -17
  1041. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  1042. package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
  1043. package/build/dist/Server/Utils/Workspace/Workspace.js +134 -41
  1044. package/build/dist/Server/Utils/Workspace/Workspace.js.map +1 -1
  1045. package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js +197 -0
  1046. package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js.map +1 -0
  1047. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js +32 -0
  1048. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
  1049. package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js +199 -0
  1050. package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js.map +1 -0
  1051. package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js +91 -0
  1052. package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js.map +1 -0
  1053. package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js +450 -0
  1054. package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js.map +1 -0
  1055. package/build/dist/Types/API/Route.js +18 -2
  1056. package/build/dist/Types/API/Route.js.map +1 -1
  1057. package/build/dist/Types/API/URL.js +23 -2
  1058. package/build/dist/Types/API/URL.js.map +1 -1
  1059. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +103 -5
  1060. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  1061. package/build/dist/Types/Date.js +97 -7
  1062. package/build/dist/Types/Date.js.map +1 -1
  1063. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  1064. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  1065. package/build/dist/Types/Exception/ExceptionMessages.js +1 -0
  1066. package/build/dist/Types/Exception/ExceptionMessages.js.map +1 -1
  1067. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +245 -0
  1068. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -0
  1069. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +49 -0
  1070. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -0
  1071. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js +16 -0
  1072. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js.map +1 -0
  1073. package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js +104 -0
  1074. package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js.map +1 -0
  1075. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +6 -5
  1076. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  1077. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js +32 -0
  1078. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js.map +1 -1
  1079. package/build/dist/Types/Monitor/MonitorUptimeSummary.js +52 -0
  1080. package/build/dist/Types/Monitor/MonitorUptimeSummary.js.map +1 -0
  1081. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +9 -1
  1082. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
  1083. package/build/dist/Types/Permission.js +84 -0
  1084. package/build/dist/Types/Permission.js.map +1 -1
  1085. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +11 -0
  1086. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  1087. package/build/dist/Types/Rules/RuleRun.js +4 -0
  1088. package/build/dist/Types/Rules/RuleRun.js.map +1 -1
  1089. package/build/dist/Types/Rum/SessionReplay.js +52 -2
  1090. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  1091. package/build/dist/Types/Rum/SessionReplayApi.js +11 -0
  1092. package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
  1093. package/build/dist/Types/Rum/UserFlow.js +115 -0
  1094. package/build/dist/Types/Rum/UserFlow.js.map +1 -0
  1095. package/build/dist/Types/Runbook/RunbookStepType.js +10 -0
  1096. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  1097. package/build/dist/Types/Runbook/RunnerJobOrigin.js +14 -0
  1098. package/build/dist/Types/Runbook/RunnerJobOrigin.js.map +1 -1
  1099. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +2 -2
  1100. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -1
  1101. package/build/dist/Types/StatusPage/MergedDowntimeTotals.js +2 -0
  1102. package/build/dist/Types/StatusPage/MergedDowntimeTotals.js.map +1 -0
  1103. package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js +65 -0
  1104. package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js.map +1 -0
  1105. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +8 -0
  1106. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  1107. package/build/dist/Types/Timezone.js +5 -0
  1108. package/build/dist/Types/Timezone.js.map +1 -1
  1109. package/build/dist/Types/TimezoneAlias.js +340 -0
  1110. package/build/dist/Types/TimezoneAlias.js.map +1 -0
  1111. package/build/dist/Types/Workflow/Templates.js +2694 -2
  1112. package/build/dist/Types/Workflow/Templates.js.map +1 -1
  1113. package/build/dist/Types/Workflow/WorkflowVariableOAuth.js +314 -0
  1114. package/build/dist/Types/Workflow/WorkflowVariableOAuth.js.map +1 -0
  1115. package/build/dist/Types/Workspace/WorkspaceNoteReaction.js +98 -0
  1116. package/build/dist/Types/Workspace/WorkspaceNoteReaction.js.map +1 -0
  1117. package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js +95 -0
  1118. package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js.map +1 -0
  1119. package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js +44 -0
  1120. package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js.map +1 -0
  1121. package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js +27 -0
  1122. package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js.map +1 -0
  1123. package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js +9 -0
  1124. package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js.map +1 -0
  1125. package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js +260 -0
  1126. package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js.map +1 -0
  1127. package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js +56 -0
  1128. package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js.map +1 -0
  1129. package/build/dist/UI/Components/Calendar/Calendar.js +94 -7
  1130. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  1131. package/build/dist/UI/Components/Calendar/CalendarDayLayout.js +136 -0
  1132. package/build/dist/UI/Components/Calendar/CalendarDayLayout.js.map +1 -0
  1133. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +1 -1
  1134. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  1135. package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js +72 -0
  1136. package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js.map +1 -0
  1137. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +178 -0
  1138. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -0
  1139. package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js +13 -1
  1140. package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js.map +1 -1
  1141. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +20 -4
  1142. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -1
  1143. package/build/dist/UI/Components/Dropdown/Dropdown.js +33 -9
  1144. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  1145. package/build/dist/UI/Components/Feed/FeedOptions.js +235 -0
  1146. package/build/dist/UI/Components/Feed/FeedOptions.js.map +1 -0
  1147. package/build/dist/UI/Components/Feed/FeedOptionsButton.js +346 -0
  1148. package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -0
  1149. package/build/dist/UI/Components/Feed/useFeedItems.js +17 -7
  1150. package/build/dist/UI/Components/Feed/useFeedItems.js.map +1 -1
  1151. package/build/dist/UI/Components/Feed/useFeedOptions.js +130 -0
  1152. package/build/dist/UI/Components/Feed/useFeedOptions.js.map +1 -0
  1153. package/build/dist/UI/Components/Forms/Fields/FormField.js +36 -13
  1154. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  1155. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +117 -11
  1156. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  1157. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +5 -1
  1158. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
  1159. package/build/dist/UI/Components/Graphs/UptimeDaySummary.js.map +1 -1
  1160. package/build/dist/UI/Components/Icon/Icon.js +1 -1
  1161. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  1162. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +36 -8
  1163. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  1164. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js +10 -2
  1165. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js.map +1 -1
  1166. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +9 -1
  1167. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
  1168. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +16 -15
  1169. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  1170. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
  1171. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  1172. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +14 -74
  1173. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  1174. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +22 -5
  1175. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  1176. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +9 -2
  1177. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
  1178. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +16 -5
  1179. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -1
  1180. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +23 -8
  1181. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  1182. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +7 -1
  1183. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  1184. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +5 -1
  1185. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
  1186. package/build/dist/UI/Components/Page/ModelPage.js +37 -3
  1187. package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
  1188. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +27 -6
  1189. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  1190. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +7 -1
  1191. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
  1192. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
  1193. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  1194. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +17 -77
  1195. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  1196. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +10 -1
  1197. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
  1198. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +15 -1
  1199. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -1
  1200. package/build/dist/UI/Utils/Timezone.js +52 -17
  1201. package/build/dist/UI/Utils/Timezone.js.map +1 -1
  1202. package/build/dist/Utils/AI/InvestigationReport.js +116 -21
  1203. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
  1204. package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js +1209 -0
  1205. package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js.map +1 -0
  1206. package/build/dist/Utils/AiRemediation/KubectlPolicy.js +2973 -0
  1207. package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -0
  1208. package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js +99 -0
  1209. package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js.map +1 -0
  1210. package/build/dist/Utils/AiRemediation/KubectlWriteScope.js +1429 -0
  1211. package/build/dist/Utils/AiRemediation/KubectlWriteScope.js.map +1 -0
  1212. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js +7 -6
  1213. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js.map +1 -1
  1214. package/build/dist/Utils/LabelRuleImportExport.js +22 -6
  1215. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
  1216. package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js +214 -0
  1217. package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js.map +1 -0
  1218. package/build/dist/Utils/Monitor/MetricValueFormatter.js +2 -2
  1219. package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js +471 -0
  1220. package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js.map +1 -0
  1221. package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js +107 -0
  1222. package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js.map +1 -0
  1223. package/build/dist/Utils/Monitor/MonitorOverviewFamily.js +191 -0
  1224. package/build/dist/Utils/Monitor/MonitorOverviewFamily.js.map +1 -0
  1225. package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js +1259 -0
  1226. package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js.map +1 -0
  1227. package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js +593 -0
  1228. package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js.map +1 -0
  1229. package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js +195 -0
  1230. package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js.map +1 -0
  1231. package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js +127 -0
  1232. package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js.map +1 -0
  1233. package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js +464 -0
  1234. package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js.map +1 -0
  1235. package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js +18 -0
  1236. package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js.map +1 -0
  1237. package/build/dist/Utils/Rum/SessionIdentity.js +31 -1
  1238. package/build/dist/Utils/Rum/SessionIdentity.js.map +1 -1
  1239. package/build/dist/Utils/Rum/SessionReplayWireEncoding.js +128 -0
  1240. package/build/dist/Utils/Rum/SessionReplayWireEncoding.js.map +1 -0
  1241. package/build/dist/Utils/Rum/UserFlow.js +941 -0
  1242. package/build/dist/Utils/Rum/UserFlow.js.map +1 -0
  1243. package/build/dist/Utils/Slo/SloDuration.js +13 -5
  1244. package/build/dist/Utils/Slo/SloDuration.js.map +1 -1
  1245. package/build/dist/Utils/Slo/SloHealth.js +8 -7
  1246. package/build/dist/Utils/Slo/SloHealth.js.map +1 -1
  1247. package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js +16 -1
  1248. package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js.map +1 -1
  1249. package/build/dist/Utils/Slo/SloOverviewText.js +16 -0
  1250. package/build/dist/Utils/Slo/SloOverviewText.js.map +1 -1
  1251. package/build/dist/Utils/Slo/SloProjection.js.map +1 -1
  1252. package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js +82 -0
  1253. package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js.map +1 -0
  1254. package/build/dist/Utils/StatusPage/ResourceUptime.js +46 -0
  1255. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  1256. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  1257. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -1
  1258. package/build/dist/Utils/Telemetry/AttributesJSON.js +245 -0
  1259. package/build/dist/Utils/Telemetry/AttributesJSON.js.map +1 -0
  1260. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js +103 -0
  1261. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js.map +1 -1
  1262. package/build/dist/Utils/Uptime/UptimeUtil.js +91 -27
  1263. package/build/dist/Utils/Uptime/UptimeUtil.js.map +1 -1
  1264. package/jest.config.json +2 -1
  1265. package/package.json +2 -1
  1266. package/tsconfig.tests.json +36 -0
  1267. package/Tests/UI/Components/EmptyState/EmptyStateGuideLinks.test.tsx +0 -337
  1268. package/UI/Components/EmptyState/EmptyStateGuideLinks.tsx +0 -124
  1269. package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js +0 -47
  1270. package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js.map +0 -1
@@ -0,0 +1,2384 @@
1
+ import {
2
+ afterEach,
3
+ beforeEach,
4
+ describe,
5
+ expect,
6
+ jest,
7
+ test,
8
+ } from "@jest/globals";
9
+ import { FindOperator } from "typeorm";
10
+ import Alert from "../../../Models/DatabaseModels/Alert";
11
+ import AlertSeverity from "../../../Models/DatabaseModels/AlertSeverity";
12
+ import AlertState from "../../../Models/DatabaseModels/AlertState";
13
+ import CephCluster from "../../../Models/DatabaseModels/CephCluster";
14
+ import DatabaseBaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
15
+ import DockerHost from "../../../Models/DatabaseModels/DockerHost";
16
+ import DockerResource from "../../../Models/DatabaseModels/DockerResource";
17
+ import DockerSwarmCluster from "../../../Models/DatabaseModels/DockerSwarmCluster";
18
+ import Host from "../../../Models/DatabaseModels/Host";
19
+ import Incident from "../../../Models/DatabaseModels/Incident";
20
+ import IncidentSeverity from "../../../Models/DatabaseModels/IncidentSeverity";
21
+ import IncidentState from "../../../Models/DatabaseModels/IncidentState";
22
+ import IncidentTemplate from "../../../Models/DatabaseModels/IncidentTemplate";
23
+ import IoTFleet from "../../../Models/DatabaseModels/IoTFleet";
24
+ import KubernetesCluster from "../../../Models/DatabaseModels/KubernetesCluster";
25
+ import KubernetesContainer from "../../../Models/DatabaseModels/KubernetesContainer";
26
+ import KubernetesResource from "../../../Models/DatabaseModels/KubernetesResource";
27
+ import Label from "../../../Models/DatabaseModels/Label";
28
+ import Monitor from "../../../Models/DatabaseModels/Monitor";
29
+ import NetworkSite from "../../../Models/DatabaseModels/NetworkSite";
30
+ import OnCallDutyPolicy from "../../../Models/DatabaseModels/OnCallDutyPolicy";
31
+ import PodmanHost from "../../../Models/DatabaseModels/PodmanHost";
32
+ import PodmanResource from "../../../Models/DatabaseModels/PodmanResource";
33
+ import ProxmoxCluster from "../../../Models/DatabaseModels/ProxmoxCluster";
34
+ import ScheduledMaintenance from "../../../Models/DatabaseModels/ScheduledMaintenance";
35
+ import ScheduledMaintenanceState from "../../../Models/DatabaseModels/ScheduledMaintenanceState";
36
+ import ScheduledMaintenanceTemplate from "../../../Models/DatabaseModels/ScheduledMaintenanceTemplate";
37
+ import ServiceModel from "../../../Models/DatabaseModels/Service";
38
+ import StatusPage from "../../../Models/DatabaseModels/StatusPage";
39
+ import VMwareVCenter from "../../../Models/DatabaseModels/VMwareVCenter";
40
+ import AlertService from "../../../Server/Services/AlertService";
41
+ import AlertSeverityService from "../../../Server/Services/AlertSeverityService";
42
+ import AlertStateService from "../../../Server/Services/AlertStateService";
43
+ import CephClusterService from "../../../Server/Services/CephClusterService";
44
+ import CustomFieldMappingService from "../../../Server/Services/CustomFieldMappingService";
45
+ import DockerHostService from "../../../Server/Services/DockerHostService";
46
+ import DockerResourceService from "../../../Server/Services/DockerResourceService";
47
+ import DockerSwarmClusterService from "../../../Server/Services/DockerSwarmClusterService";
48
+ import HostService from "../../../Server/Services/HostService";
49
+ import IncidentService from "../../../Server/Services/IncidentService";
50
+ import IncidentSeverityService from "../../../Server/Services/IncidentSeverityService";
51
+ import IncidentStateService from "../../../Server/Services/IncidentStateService";
52
+ import IncidentTemplateService from "../../../Server/Services/IncidentTemplateService";
53
+ import IoTFleetService from "../../../Server/Services/IoTFleetService";
54
+ import KubernetesClusterService from "../../../Server/Services/KubernetesClusterService";
55
+ import KubernetesContainerService from "../../../Server/Services/KubernetesContainerService";
56
+ import KubernetesResourceService from "../../../Server/Services/KubernetesResourceService";
57
+ import LabelService from "../../../Server/Services/LabelService";
58
+ import MonitorService from "../../../Server/Services/MonitorService";
59
+ import NetworkSiteService from "../../../Server/Services/NetworkSiteService";
60
+ import OnCallDutyPolicyService from "../../../Server/Services/OnCallDutyPolicyService";
61
+ import PodmanHostService from "../../../Server/Services/PodmanHostService";
62
+ import PodmanResourceService from "../../../Server/Services/PodmanResourceService";
63
+ import ProjectService from "../../../Server/Services/ProjectService";
64
+ import ProxmoxClusterService from "../../../Server/Services/ProxmoxClusterService";
65
+ import ScheduledMaintenanceService from "../../../Server/Services/ScheduledMaintenanceService";
66
+ import ScheduledMaintenanceStateService from "../../../Server/Services/ScheduledMaintenanceStateService";
67
+ import ScheduledMaintenanceTemplateService from "../../../Server/Services/ScheduledMaintenanceTemplateService";
68
+ import ServiceService from "../../../Server/Services/ServiceService";
69
+ import StatusPageService from "../../../Server/Services/StatusPageService";
70
+ import VMwareVCenterService from "../../../Server/Services/VMwareVCenterService";
71
+ import DatabaseService from "../../../Server/Services/DatabaseService";
72
+ import { ProjectScopedRelation } from "../../../Server/Utils/Database/ProjectScopedReferenceValidator";
73
+ import { TableColumnMetadata } from "../../../Types/Database/TableColumn";
74
+ import TableColumnType from "../../../Types/Database/TableColumnType";
75
+ import Dictionary from "../../../Types/Dictionary";
76
+ import ObjectID from "../../../Types/ObjectID";
77
+
78
+ /*
79
+ * Incidents, alerts and scheduled maintenance events carry many-to-many
80
+ * lists — monitors, labels, on-call policies, status pages — that nothing
81
+ * checked against the record's project. DatabaseService turns each id into a join row
82
+ * and the permission layer never looks at relation ids, so an API caller in
83
+ * one project could attach another project's records. On create,
84
+ * IncidentService then changed the status of those foreign monitors and
85
+ * executed the foreign on-call policies for this project's incident, and
86
+ * AlertService did the same with an alert's on-call policies.
87
+ *
88
+ * Alerts and incidents also carry the affected-resource lists — hosts,
89
+ * clusters, container hosts, services and the rest — that the dashboard's
90
+ * resource picker edits. A foreign id there put this project's alert on the
91
+ * other project's resource Activity tab and badge counts, and put the other
92
+ * project's resource names into this project's views, which read the
93
+ * relation as root.
94
+ *
95
+ * Scheduled maintenance events carry most of those resource lists too (plus
96
+ * network sites), and incident and scheduled maintenance templates carry a
97
+ * handful. A template's lists are copied onto every incident or event made
98
+ * from it, where they are checked, so a foreign id saved on a template made
99
+ * every create from that template fail.
100
+ *
101
+ * These tests drive the real ProjectScopedReferenceValidator through the
102
+ * service hooks and only stub the lookups it makes, so a foreign or unknown id
103
+ * has to be caught by the actual check. Rejections assert the record's name
104
+ * as the lookup returned it: that name only exists on the record registered
105
+ * under the right service, so validating labels against the monitor table
106
+ * (say) cannot pass them.
107
+ */
108
+
109
+ const PROJECT_ID: ObjectID = new ObjectID(
110
+ "4af3a31b-58b0-4746-8025-f9cd4db1945e",
111
+ );
112
+ const OTHER_PROJECT_ID: ObjectID = new ObjectID(
113
+ "3376855b-361c-427c-8982-bad7ada30414",
114
+ );
115
+
116
+ const OWN_MONITOR_ID: string = "0e7c5d0e-8b44-4b53-9d3c-0f4e7a1b2c01";
117
+ const SECOND_OWN_MONITOR_ID: string = "0e7c5d0e-8b44-4b53-9d3c-0f4e7a1b2c02";
118
+ const FOREIGN_MONITOR_ID: string = "b1d0f6c2-3a5e-4c7d-8e9f-a0b1c2d3e4f5";
119
+ const OWN_LABEL_ID: string = "5f1e2d3c-4b5a-4968-8776-655443322110";
120
+ const FOREIGN_LABEL_ID: string = "6a7b8c9d-0e1f-4a2b-9c3d-4e5f6a7b8c9d";
121
+ const OWN_POLICY_ID: string = "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a";
122
+ const FOREIGN_POLICY_ID: string = "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f";
123
+ const OWN_STATUS_PAGE_ID: string = "7e6d5c4b-3a29-4180-9f8e-7d6c5b4a3928";
124
+ const FOREIGN_STATUS_PAGE_ID: string = "8f7e6d5c-4b3a-4291-8e0f-9a8b7c6d5e4f";
125
+ const UNKNOWN_ID: string = "9c0ba0b3-2f8e-4c02-a8d5-6a4d2f5b9c11";
126
+ const STATE_ID: string = "2b0a94a4-2f8c-49f0-8a2e-0f1ff5df41c9";
127
+ const SEVERITY_ID: string = "6a56b0f9-6c8f-4f76-9b53-0a1a5b0ec1a2";
128
+ const TEMPLATE_ID: string = "1d2c3b4a-5968-4776-8a5b-4c3d2e1f0a9b";
129
+ const SECOND_TEMPLATE_ID: string = "1d2c3b4a-5968-4776-8a5b-4c3d2e1f0a9c";
130
+ const ALERT_SEVERITY_ID: string = "7b1c2d3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e";
131
+ const INCIDENT_ID: string = "a2eb67d4-bd2e-4186-9187-dad799c9316c";
132
+ const SECOND_INCIDENT_ID: string = "a2eb67d4-bd2e-4186-9187-dad799c9316d";
133
+ const EVENT_ID: string = "c3d4e5f6-a7b8-4c9d-8e0f-1a2b3c4d5e6f";
134
+ const ALERT_ID: string = "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a";
135
+ const SECOND_ALERT_ID: string = "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7b";
136
+
137
+ /*
138
+ * Every affected-resource list alerts and incidents share, with one record
139
+ * in this project and one in another project for each.
140
+ */
141
+ interface ResourceList {
142
+ column: string;
143
+ modelName: string;
144
+ model: new () => DatabaseBaseModel;
145
+ service: { findBy: unknown };
146
+ ownId: string;
147
+ foreignId: string;
148
+ }
149
+
150
+ function resourceList(
151
+ index: number,
152
+ column: string,
153
+ modelName: string,
154
+ model: new () => DatabaseBaseModel,
155
+ service: { findBy: unknown },
156
+ ): ResourceList {
157
+ const suffix: string = index.toString().padStart(12, "0");
158
+
159
+ return {
160
+ column: column,
161
+ modelName: modelName,
162
+ model: model,
163
+ service: service,
164
+ ownId: `e0a1b2c3-d4e5-4f60-8a1b-${suffix}`,
165
+ foreignId: `e1a1b2c3-d4e5-4f60-8a1b-${suffix}`,
166
+ };
167
+ }
168
+
169
+ const RESOURCE_LISTS: Array<ResourceList> = [
170
+ resourceList(1, "hosts", "Host", Host, HostService),
171
+ resourceList(
172
+ 2,
173
+ "kubernetesClusters",
174
+ "Kubernetes Cluster",
175
+ KubernetesCluster,
176
+ KubernetesClusterService,
177
+ ),
178
+ resourceList(
179
+ 3,
180
+ "kubernetesResources",
181
+ "Kubernetes Resource",
182
+ KubernetesResource,
183
+ KubernetesResourceService,
184
+ ),
185
+ resourceList(
186
+ 4,
187
+ "kubernetesContainers",
188
+ "Kubernetes Container",
189
+ KubernetesContainer,
190
+ KubernetesContainerService,
191
+ ),
192
+ resourceList(5, "dockerHosts", "Docker Host", DockerHost, DockerHostService),
193
+ resourceList(6, "podmanHosts", "Podman Host", PodmanHost, PodmanHostService),
194
+ resourceList(
195
+ 7,
196
+ "proxmoxClusters",
197
+ "Proxmox Cluster",
198
+ ProxmoxCluster,
199
+ ProxmoxClusterService,
200
+ ),
201
+ resourceList(
202
+ 8,
203
+ "vmwareVCenters",
204
+ "VMware vCenter",
205
+ VMwareVCenter,
206
+ VMwareVCenterService,
207
+ ),
208
+ resourceList(9, "iotFleets", "IoT Fleet", IoTFleet, IoTFleetService),
209
+ resourceList(
210
+ 10,
211
+ "dockerSwarmClusters",
212
+ "Docker Swarm Cluster",
213
+ DockerSwarmCluster,
214
+ DockerSwarmClusterService,
215
+ ),
216
+ resourceList(
217
+ 11,
218
+ "cephClusters",
219
+ "Ceph Cluster",
220
+ CephCluster,
221
+ CephClusterService,
222
+ ),
223
+ resourceList(
224
+ 12,
225
+ "dockerResources",
226
+ "Docker Resource",
227
+ DockerResource,
228
+ DockerResourceService,
229
+ ),
230
+ resourceList(
231
+ 13,
232
+ "podmanResources",
233
+ "Podman Resource",
234
+ PodmanResource,
235
+ PodmanResourceService,
236
+ ),
237
+ resourceList(14, "services", "Service", ServiceModel, ServiceService),
238
+ ];
239
+
240
+ // Only scheduled maintenance events carry this one.
241
+ const NETWORK_SITE_LIST: ResourceList = resourceList(
242
+ 15,
243
+ "networkSites",
244
+ "Network Site",
245
+ NetworkSite,
246
+ NetworkSiteService,
247
+ );
248
+
249
+ const ALL_RESOURCE_LISTS: Array<ResourceList> = [
250
+ ...RESOURCE_LISTS,
251
+ NETWORK_SITE_LIST,
252
+ ];
253
+
254
+ function listsNamed(columns: Array<string>): Array<ResourceList> {
255
+ return columns.map((column: string) => {
256
+ return ALL_RESOURCE_LISTS.find((list: ResourceList) => {
257
+ return list.column === column;
258
+ })!;
259
+ });
260
+ }
261
+
262
+ // The affected-resource lists a scheduled maintenance event carries.
263
+ const EVENT_RESOURCE_LISTS: Array<ResourceList> = listsNamed([
264
+ "hosts",
265
+ "kubernetesClusters",
266
+ "dockerHosts",
267
+ "podmanHosts",
268
+ "proxmoxClusters",
269
+ "vmwareVCenters",
270
+ "iotFleets",
271
+ "networkSites",
272
+ "dockerSwarmClusters",
273
+ "cephClusters",
274
+ "services",
275
+ ]);
276
+
277
+ // The affected-resource lists incident and scheduled maintenance templates carry.
278
+ const TEMPLATE_RESOURCE_LISTS: Array<ResourceList> = listsNamed([
279
+ "hosts",
280
+ "kubernetesClusters",
281
+ "dockerHosts",
282
+ "podmanHosts",
283
+ "services",
284
+ ]);
285
+
286
+ /*
287
+ * The monitor, label, on-call policy and status page lists, in the same
288
+ * shape, for the templates' tables below.
289
+ */
290
+ function relationList(
291
+ column: string,
292
+ modelName: string,
293
+ model: new () => DatabaseBaseModel,
294
+ service: { findBy: unknown },
295
+ ownId: string,
296
+ foreignId: string,
297
+ ): ResourceList {
298
+ return {
299
+ column: column,
300
+ modelName: modelName,
301
+ model: model,
302
+ service: service,
303
+ ownId: ownId,
304
+ foreignId: foreignId,
305
+ };
306
+ }
307
+
308
+ const MONITOR_LIST: ResourceList = relationList(
309
+ "monitors",
310
+ "Monitor",
311
+ Monitor,
312
+ MonitorService,
313
+ OWN_MONITOR_ID,
314
+ FOREIGN_MONITOR_ID,
315
+ );
316
+
317
+ const LABEL_LIST: ResourceList = relationList(
318
+ "labels",
319
+ "Label",
320
+ Label,
321
+ LabelService,
322
+ OWN_LABEL_ID,
323
+ FOREIGN_LABEL_ID,
324
+ );
325
+
326
+ const POLICY_LIST: ResourceList = relationList(
327
+ "onCallDutyPolicies",
328
+ "On-Call Policy",
329
+ OnCallDutyPolicy,
330
+ OnCallDutyPolicyService,
331
+ OWN_POLICY_ID,
332
+ FOREIGN_POLICY_ID,
333
+ );
334
+
335
+ const STATUS_PAGE_LIST: ResourceList = relationList(
336
+ "statusPages",
337
+ "Status Page",
338
+ StatusPage,
339
+ StatusPageService,
340
+ OWN_STATUS_PAGE_ID,
341
+ FOREIGN_STATUS_PAGE_ID,
342
+ );
343
+
344
+ // Every list an incident template carries.
345
+ const INCIDENT_TEMPLATE_LISTS: Array<ResourceList> = [
346
+ MONITOR_LIST,
347
+ LABEL_LIST,
348
+ POLICY_LIST,
349
+ ...TEMPLATE_RESOURCE_LISTS,
350
+ ];
351
+
352
+ // Every list a scheduled maintenance template carries.
353
+ const EVENT_TEMPLATE_LISTS: Array<ResourceList> = [
354
+ MONITOR_LIST,
355
+ LABEL_LIST,
356
+ STATUS_PAGE_LIST,
357
+ ...TEMPLATE_RESOURCE_LISTS,
358
+ ];
359
+
360
+ function hostList(): ResourceList {
361
+ return RESOURCE_LISTS[0]!;
362
+ }
363
+
364
+ /*
365
+ * The payload for one resource per list, `pick` choosing which of the two
366
+ * records. Relation stubs, like an API create sends them.
367
+ */
368
+ function resourcesOf(
369
+ pick: (list: ResourceList) => string,
370
+ lists: Array<ResourceList> = RESOURCE_LISTS,
371
+ ): Dictionary<Array<DatabaseBaseModel>> {
372
+ const payload: Dictionary<Array<DatabaseBaseModel>> = {};
373
+
374
+ for (const list of lists) {
375
+ payload[list.column] = [stubOf(list.model, pick(list))];
376
+ }
377
+
378
+ return payload;
379
+ }
380
+
381
+ // Asserts one error names every foreign resource.
382
+ async function expectEveryForeignResourceNamed(
383
+ write: Promise<unknown>,
384
+ lists: Array<ResourceList> = RESOURCE_LISTS,
385
+ ): Promise<void> {
386
+ let message: string = "";
387
+
388
+ try {
389
+ await write;
390
+ } catch (err) {
391
+ message = (err as Error).message;
392
+ }
393
+
394
+ expect(message).toContain("belong to a different project: ");
395
+
396
+ for (const list of lists) {
397
+ expect(message).toContain(`${list.modelName} "${nameOf(list.foreignId)}"`);
398
+ }
399
+ }
400
+
401
+ function expectNoResourceLookedUp(): void {
402
+ for (const list of ALL_RESOURCE_LISTS) {
403
+ expect(list.service.findBy).not.toHaveBeenCalled();
404
+ }
405
+ }
406
+
407
+ function nameOf(id: string): string {
408
+ return `record ${id}`;
409
+ }
410
+
411
+ function withProject<T extends DatabaseBaseModel>(
412
+ model: T,
413
+ id: string,
414
+ projectId: ObjectID,
415
+ ): T {
416
+ model._id = id;
417
+ model.setValue("projectId", projectId);
418
+ model.setValue("name", nameOf(id));
419
+ return model;
420
+ }
421
+
422
+ function stubOf<T extends DatabaseBaseModel>(ctor: new () => T, id: string): T {
423
+ const model: T = new ctor();
424
+ model._id = id;
425
+ return model;
426
+ }
427
+
428
+ // Stand-in for the database: every record the validator can find, by service.
429
+ function registerRecords(): void {
430
+ const byService: Array<{
431
+ service: { findBy: unknown };
432
+ records: Array<DatabaseBaseModel>;
433
+ }> = [
434
+ {
435
+ service: MonitorService,
436
+ records: [
437
+ withProject(new Monitor(), OWN_MONITOR_ID, PROJECT_ID),
438
+ withProject(new Monitor(), SECOND_OWN_MONITOR_ID, PROJECT_ID),
439
+ withProject(new Monitor(), FOREIGN_MONITOR_ID, OTHER_PROJECT_ID),
440
+ ],
441
+ },
442
+ {
443
+ service: LabelService,
444
+ records: [
445
+ withProject(new Label(), OWN_LABEL_ID, PROJECT_ID),
446
+ withProject(new Label(), FOREIGN_LABEL_ID, OTHER_PROJECT_ID),
447
+ ],
448
+ },
449
+ {
450
+ service: OnCallDutyPolicyService,
451
+ records: [
452
+ withProject(new OnCallDutyPolicy(), OWN_POLICY_ID, PROJECT_ID),
453
+ withProject(
454
+ new OnCallDutyPolicy(),
455
+ FOREIGN_POLICY_ID,
456
+ OTHER_PROJECT_ID,
457
+ ),
458
+ ],
459
+ },
460
+ {
461
+ service: StatusPageService,
462
+ records: [
463
+ withProject(new StatusPage(), OWN_STATUS_PAGE_ID, PROJECT_ID),
464
+ withProject(new StatusPage(), FOREIGN_STATUS_PAGE_ID, OTHER_PROJECT_ID),
465
+ ],
466
+ },
467
+ {
468
+ service: IncidentStateService,
469
+ records: [withProject(new IncidentState(), STATE_ID, PROJECT_ID)],
470
+ },
471
+ {
472
+ service: IncidentSeverityService,
473
+ records: [withProject(new IncidentSeverity(), SEVERITY_ID, PROJECT_ID)],
474
+ },
475
+ {
476
+ service: AlertSeverityService,
477
+ records: [
478
+ withProject(new AlertSeverity(), ALERT_SEVERITY_ID, PROJECT_ID),
479
+ ],
480
+ },
481
+ ...ALL_RESOURCE_LISTS.map((list: ResourceList) => {
482
+ return {
483
+ service: list.service,
484
+ records: [
485
+ withProject(new list.model(), list.ownId, PROJECT_ID),
486
+ withProject(new list.model(), list.foreignId, OTHER_PROJECT_ID),
487
+ ],
488
+ };
489
+ }),
490
+ ];
491
+
492
+ for (const { service, records } of byService) {
493
+ jest
494
+ .spyOn(service as never, "findBy" as never)
495
+ .mockImplementation((async (findBy: {
496
+ query: { _id?: unknown };
497
+ }): Promise<Array<DatabaseBaseModel>> => {
498
+ // The validator looks ids up with QueryHelper.any (a Raw IN operator).
499
+ const idFilter: FindOperator<unknown> = findBy.query
500
+ ._id as FindOperator<unknown>;
501
+ const requestedIds: Array<string> = (
502
+ Object.values(idFilter.objectLiteralParameters || {}) as Array<
503
+ Array<string>
504
+ >
505
+ )
506
+ .flat()
507
+ .map((id: string) => {
508
+ return id.toLowerCase();
509
+ });
510
+
511
+ return records.filter((record: DatabaseBaseModel) => {
512
+ return requestedIds.includes(record._id!.toLowerCase());
513
+ });
514
+ }) as never);
515
+ }
516
+ }
517
+
518
+ function callHook(
519
+ service: unknown,
520
+ hook: "onBeforeCreate" | "onBeforeUpdate",
521
+ payload: unknown,
522
+ ): Promise<unknown> {
523
+ return (service as Record<string, (input: unknown) => Promise<unknown>>)[
524
+ hook
525
+ ]!(payload);
526
+ }
527
+
528
+ function foreignMessage(modelName: string, id: string): string {
529
+ return `belong to a different project: ${modelName} "${nameOf(id)}"`;
530
+ }
531
+
532
+ describe("cross-project relation guard on write", () => {
533
+ beforeEach(() => {
534
+ registerRecords();
535
+
536
+ jest
537
+ .spyOn(CustomFieldMappingService, "applyMappingsToCreate")
538
+ .mockResolvedValue(undefined as never);
539
+ jest
540
+ .spyOn(CustomFieldMappingService, "applyMappingsToUpdate")
541
+ .mockResolvedValue(undefined as never);
542
+ });
543
+
544
+ afterEach(() => {
545
+ jest.restoreAllMocks();
546
+ });
547
+
548
+ describe("IncidentService create", () => {
549
+ let counter: jest.Mock;
550
+
551
+ beforeEach(() => {
552
+ const createdState: IncidentState = new IncidentState();
553
+ createdState._id = STATE_ID;
554
+ jest
555
+ .spyOn(IncidentStateService, "findOneBy")
556
+ .mockResolvedValue(createdState as never);
557
+
558
+ counter = jest.fn(async () => {
559
+ return { counter: 1, prefix: undefined };
560
+ }) as unknown as jest.Mock;
561
+ jest
562
+ .spyOn(ProjectService, "incrementAndGetIncidentCounter")
563
+ .mockImplementation(counter as never);
564
+ });
565
+
566
+ function incidentWith(relations: Partial<Incident>): Incident {
567
+ const incident: Incident = new Incident();
568
+ incident.title = "test";
569
+ incident.incidentSeverityId = new ObjectID(SEVERITY_ID);
570
+ Object.assign(incident, relations);
571
+ return incident;
572
+ }
573
+
574
+ test("rejects another project's monitor before it can change that monitor's status", async () => {
575
+ await expect(
576
+ callHook(IncidentService, "onBeforeCreate", {
577
+ data: incidentWith({
578
+ monitors: [stubOf(Monitor, FOREIGN_MONITOR_ID)],
579
+ }),
580
+ props: { tenantId: PROJECT_ID },
581
+ }),
582
+ ).rejects.toThrow(foreignMessage("Monitor", FOREIGN_MONITOR_ID));
583
+
584
+ // Rejected before the counter, so no incident number is burned.
585
+ expect(counter).not.toHaveBeenCalled();
586
+ });
587
+
588
+ test("rejects another project's label", async () => {
589
+ await expect(
590
+ callHook(IncidentService, "onBeforeCreate", {
591
+ data: incidentWith({ labels: [stubOf(Label, FOREIGN_LABEL_ID)] }),
592
+ props: { tenantId: PROJECT_ID },
593
+ }),
594
+ ).rejects.toThrow(foreignMessage("Label", FOREIGN_LABEL_ID));
595
+ });
596
+
597
+ test("rejects another project's on-call policy before it can be executed", async () => {
598
+ await expect(
599
+ callHook(IncidentService, "onBeforeCreate", {
600
+ data: incidentWith({
601
+ onCallDutyPolicies: [stubOf(OnCallDutyPolicy, FOREIGN_POLICY_ID)],
602
+ }),
603
+ props: { tenantId: PROJECT_ID },
604
+ }),
605
+ ).rejects.toThrow(foreignMessage("On-Call Policy", FOREIGN_POLICY_ID));
606
+ });
607
+
608
+ test("names every foreign record in one error", async () => {
609
+ await expect(
610
+ callHook(IncidentService, "onBeforeCreate", {
611
+ data: incidentWith({
612
+ monitors: [
613
+ stubOf(Monitor, OWN_MONITOR_ID),
614
+ stubOf(Monitor, FOREIGN_MONITOR_ID),
615
+ ],
616
+ labels: [stubOf(Label, FOREIGN_LABEL_ID)],
617
+ onCallDutyPolicies: [stubOf(OnCallDutyPolicy, FOREIGN_POLICY_ID)],
618
+ }),
619
+ props: { tenantId: PROJECT_ID },
620
+ }),
621
+ ).rejects.toThrow(
622
+ `belong to a different project: Monitor "${nameOf(
623
+ FOREIGN_MONITOR_ID,
624
+ )}", Label "${nameOf(FOREIGN_LABEL_ID)}", On-Call Policy "${nameOf(
625
+ FOREIGN_POLICY_ID,
626
+ )}"`,
627
+ );
628
+ });
629
+
630
+ test("rejects a monitor id that matches no record", async () => {
631
+ await expect(
632
+ callHook(IncidentService, "onBeforeCreate", {
633
+ data: incidentWith({ monitors: [stubOf(Monitor, UNKNOWN_ID)] }),
634
+ props: { tenantId: PROJECT_ID },
635
+ }),
636
+ ).rejects.toThrow(`do not exist: Monitor "${UNKNOWN_ID}"`);
637
+ });
638
+
639
+ test("root creates are checked against the project on the payload", async () => {
640
+ /*
641
+ * Slack, Teams and the workers create as root with projectId on the
642
+ * data rather than a tenant on the props.
643
+ */
644
+ const incident: Incident = incidentWith({
645
+ onCallDutyPolicies: [stubOf(OnCallDutyPolicy, FOREIGN_POLICY_ID)],
646
+ });
647
+ incident.projectId = PROJECT_ID;
648
+
649
+ await expect(
650
+ callHook(IncidentService, "onBeforeCreate", {
651
+ data: incident,
652
+ props: { isRoot: true },
653
+ }),
654
+ ).rejects.toThrow(foreignMessage("On-Call Policy", FOREIGN_POLICY_ID));
655
+ });
656
+
657
+ test("relations copied from an incident template are checked too", async () => {
658
+ /*
659
+ * A caller that sends only createdIncidentTemplateId has the server
660
+ * copy the template's lists onto the incident. Templates never had
661
+ * their relations checked, so the copy is validated like any other
662
+ * payload rather than trusted.
663
+ */
664
+ const template: IncidentTemplate = new IncidentTemplate();
665
+ template._id = TEMPLATE_ID;
666
+ template.monitors = [stubOf(Monitor, FOREIGN_MONITOR_ID)];
667
+
668
+ jest
669
+ .spyOn(IncidentTemplateService, "findOneBy")
670
+ .mockResolvedValue(template as never);
671
+
672
+ await expect(
673
+ callHook(IncidentService, "onBeforeCreate", {
674
+ data: incidentWith({
675
+ createdIncidentTemplateId: TEMPLATE_ID,
676
+ }),
677
+ props: { tenantId: PROJECT_ID },
678
+ }),
679
+ ).rejects.toThrow(foreignMessage("Monitor", FOREIGN_MONITOR_ID));
680
+
681
+ expect(counter).not.toHaveBeenCalled();
682
+ });
683
+
684
+ test("accepts this project's monitors, labels and on-call policies in every shape", async () => {
685
+ const incident: Incident = incidentWith({
686
+ monitors: [
687
+ stubOf(Monitor, OWN_MONITOR_ID),
688
+ // A bare uuid string, as an API client may send it.
689
+ SECOND_OWN_MONITOR_ID as unknown as Monitor,
690
+ ],
691
+ labels: [{ _id: OWN_LABEL_ID } as unknown as Label],
692
+ onCallDutyPolicies: [
693
+ // Upper case: Postgres reads uuids back lower-cased.
694
+ stubOf(OnCallDutyPolicy, OWN_POLICY_ID.toUpperCase()),
695
+ ],
696
+ });
697
+
698
+ await expect(
699
+ callHook(IncidentService, "onBeforeCreate", {
700
+ data: incident,
701
+ props: { tenantId: PROJECT_ID },
702
+ }),
703
+ ).resolves.toBeDefined();
704
+
705
+ expect(counter).toHaveBeenCalledTimes(1);
706
+ });
707
+
708
+ test("does not look relations up when none are sent", async () => {
709
+ await callHook(IncidentService, "onBeforeCreate", {
710
+ data: incidentWith({}),
711
+ props: { tenantId: PROJECT_ID },
712
+ });
713
+
714
+ expect(MonitorService.findBy).not.toHaveBeenCalled();
715
+ expect(LabelService.findBy).not.toHaveBeenCalled();
716
+ expect(OnCallDutyPolicyService.findBy).not.toHaveBeenCalled();
717
+ expectNoResourceLookedUp();
718
+ });
719
+
720
+ test.each(RESOURCE_LISTS)(
721
+ "rejects another project's $modelName",
722
+ async (list: ResourceList) => {
723
+ await expect(
724
+ callHook(IncidentService, "onBeforeCreate", {
725
+ data: incidentWith({
726
+ [list.column]: [stubOf(list.model, list.foreignId)],
727
+ } as Partial<Incident>),
728
+ props: { tenantId: PROJECT_ID },
729
+ }),
730
+ ).rejects.toThrow(foreignMessage(list.modelName, list.foreignId));
731
+
732
+ expect(counter).not.toHaveBeenCalled();
733
+ },
734
+ );
735
+
736
+ test("names every foreign affected resource in one error", async () => {
737
+ await expectEveryForeignResourceNamed(
738
+ callHook(IncidentService, "onBeforeCreate", {
739
+ data: incidentWith(
740
+ resourcesOf((list: ResourceList) => {
741
+ return list.foreignId;
742
+ }) as Partial<Incident>,
743
+ ),
744
+ props: { tenantId: PROJECT_ID },
745
+ }),
746
+ );
747
+
748
+ expect(counter).not.toHaveBeenCalled();
749
+ });
750
+
751
+ test("rejects an affected resource id that matches no record", async () => {
752
+ await expect(
753
+ callHook(IncidentService, "onBeforeCreate", {
754
+ data: incidentWith({ hosts: [stubOf(Host, UNKNOWN_ID)] }),
755
+ props: { tenantId: PROJECT_ID },
756
+ }),
757
+ ).rejects.toThrow(`do not exist: Host "${UNKNOWN_ID}"`);
758
+ });
759
+
760
+ test("affected resources copied from an incident template are checked too", async () => {
761
+ const template: IncidentTemplate = new IncidentTemplate();
762
+ template._id = TEMPLATE_ID;
763
+ template.hosts = [stubOf(Host, hostList().foreignId)];
764
+
765
+ jest
766
+ .spyOn(IncidentTemplateService, "findOneBy")
767
+ .mockResolvedValue(template as never);
768
+
769
+ await expect(
770
+ callHook(IncidentService, "onBeforeCreate", {
771
+ data: incidentWith({
772
+ createdIncidentTemplateId: TEMPLATE_ID,
773
+ }),
774
+ props: { tenantId: PROJECT_ID },
775
+ }),
776
+ ).rejects.toThrow(foreignMessage("Host", hostList().foreignId));
777
+
778
+ expect(counter).not.toHaveBeenCalled();
779
+ });
780
+
781
+ test("accepts this project's record on every affected-resource list", async () => {
782
+ await expect(
783
+ callHook(IncidentService, "onBeforeCreate", {
784
+ data: incidentWith(
785
+ resourcesOf((list: ResourceList) => {
786
+ return list.ownId;
787
+ }) as Partial<Incident>,
788
+ ),
789
+ props: { tenantId: PROJECT_ID },
790
+ }),
791
+ ).resolves.toBeDefined();
792
+
793
+ expect(counter).toHaveBeenCalledTimes(1);
794
+ });
795
+ });
796
+
797
+ describe("IncidentService update", () => {
798
+ /*
799
+ * The rows the update query matches, as the hook reads them back. Each
800
+ * test sets what they already store.
801
+ */
802
+ let matchedIncidents: Array<Incident> = [];
803
+
804
+ beforeEach(() => {
805
+ matchedIncidents = [];
806
+
807
+ jest.spyOn(IncidentService, "findBy").mockImplementation((async () => {
808
+ return matchedIncidents;
809
+ }) as never);
810
+ });
811
+
812
+ function storedIncident(data: {
813
+ id: string;
814
+ projectId: ObjectID;
815
+ monitorIds?: Array<string>;
816
+ labelIds?: Array<string>;
817
+ policyIds?: Array<string>;
818
+ resourceIds?: Dictionary<Array<string>>;
819
+ }): Incident {
820
+ const incident: Incident = new Incident();
821
+ incident._id = data.id;
822
+ incident.projectId = data.projectId;
823
+ incident.monitors = (data.monitorIds || []).map((id: string) => {
824
+ return stubOf(Monitor, id);
825
+ });
826
+ incident.labels = (data.labelIds || []).map((id: string) => {
827
+ return stubOf(Label, id);
828
+ });
829
+ incident.onCallDutyPolicies = (data.policyIds || []).map((id: string) => {
830
+ return stubOf(OnCallDutyPolicy, id);
831
+ });
832
+ for (const list of RESOURCE_LISTS) {
833
+ Object.assign(incident, {
834
+ [list.column]: (data.resourceIds?.[list.column] || []).map(
835
+ (id: string) => {
836
+ return stubOf(list.model, id);
837
+ },
838
+ ),
839
+ });
840
+ }
841
+ return incident;
842
+ }
843
+
844
+ test("rejects adding another project's monitor", async () => {
845
+ matchedIncidents = [
846
+ storedIncident({
847
+ id: INCIDENT_ID,
848
+ projectId: PROJECT_ID,
849
+ monitorIds: [OWN_MONITOR_ID],
850
+ }),
851
+ ];
852
+
853
+ await expect(
854
+ callHook(IncidentService, "onBeforeUpdate", {
855
+ data: {
856
+ monitors: [OWN_MONITOR_ID, FOREIGN_MONITOR_ID],
857
+ },
858
+ query: { _id: INCIDENT_ID },
859
+ props: { tenantId: PROJECT_ID },
860
+ }),
861
+ ).rejects.toThrow(foreignMessage("Monitor", FOREIGN_MONITOR_ID));
862
+ });
863
+
864
+ test("rejects adding another project's label or on-call policy", async () => {
865
+ matchedIncidents = [
866
+ storedIncident({ id: INCIDENT_ID, projectId: PROJECT_ID }),
867
+ ];
868
+
869
+ await expect(
870
+ callHook(IncidentService, "onBeforeUpdate", {
871
+ data: {
872
+ labels: [{ _id: FOREIGN_LABEL_ID }],
873
+ onCallDutyPolicies: [new ObjectID(FOREIGN_POLICY_ID)],
874
+ },
875
+ query: { _id: INCIDENT_ID },
876
+ props: { tenantId: PROJECT_ID },
877
+ }),
878
+ ).rejects.toThrow(
879
+ `belong to a different project: Label "${nameOf(
880
+ FOREIGN_LABEL_ID,
881
+ )}", On-Call Policy "${nameOf(FOREIGN_POLICY_ID)}"`,
882
+ );
883
+ });
884
+
885
+ test("re-saving a list keeps an id the incident already holds", async () => {
886
+ /*
887
+ * Incidents created before this guard — or by a monitor whose criteria
888
+ * still carried a stale label — can already hold another project's
889
+ * record. Refusing to save the list they already have would lock the
890
+ * user out of editing it; only ids the update adds are checked.
891
+ */
892
+ matchedIncidents = [
893
+ storedIncident({
894
+ id: INCIDENT_ID,
895
+ projectId: PROJECT_ID,
896
+ labelIds: [FOREIGN_LABEL_ID],
897
+ }),
898
+ ];
899
+
900
+ await expect(
901
+ callHook(IncidentService, "onBeforeUpdate", {
902
+ data: {
903
+ labels: [FOREIGN_LABEL_ID.toUpperCase(), OWN_LABEL_ID],
904
+ },
905
+ query: { _id: INCIDENT_ID },
906
+ props: { tenantId: PROJECT_ID },
907
+ }),
908
+ ).resolves.toBeDefined();
909
+
910
+ // Only the added label was looked up.
911
+ expect(LabelService.findBy).toHaveBeenCalledTimes(1);
912
+ });
913
+
914
+ test("an id held by one matched incident is still checked for the others", async () => {
915
+ /*
916
+ * A bulk update writes the same list onto every matched incident, so a
917
+ * foreign id is only exempt when every one of them already holds it.
918
+ */
919
+ matchedIncidents = [
920
+ storedIncident({
921
+ id: INCIDENT_ID,
922
+ projectId: PROJECT_ID,
923
+ policyIds: [FOREIGN_POLICY_ID],
924
+ }),
925
+ storedIncident({ id: SECOND_INCIDENT_ID, projectId: PROJECT_ID }),
926
+ ];
927
+
928
+ await expect(
929
+ callHook(IncidentService, "onBeforeUpdate", {
930
+ data: {
931
+ onCallDutyPolicies: [FOREIGN_POLICY_ID],
932
+ },
933
+ query: {},
934
+ props: { tenantId: PROJECT_ID },
935
+ }),
936
+ ).rejects.toThrow(foreignMessage("On-Call Policy", FOREIGN_POLICY_ID));
937
+ });
938
+
939
+ test("an incident from another project does not exempt an id for this one", async () => {
940
+ // The read runs as root, so it can see rows outside the tenant.
941
+ matchedIncidents = [
942
+ storedIncident({
943
+ id: SECOND_INCIDENT_ID,
944
+ projectId: OTHER_PROJECT_ID,
945
+ monitorIds: [FOREIGN_MONITOR_ID],
946
+ }),
947
+ ];
948
+
949
+ await expect(
950
+ callHook(IncidentService, "onBeforeUpdate", {
951
+ data: { monitors: [FOREIGN_MONITOR_ID] },
952
+ query: {},
953
+ props: { tenantId: PROJECT_ID },
954
+ }),
955
+ ).rejects.toThrow(foreignMessage("Monitor", FOREIGN_MONITOR_ID));
956
+ });
957
+
958
+ test("updates without a tenant are checked against each matched incident's project", async () => {
959
+ matchedIncidents = [
960
+ storedIncident({ id: INCIDENT_ID, projectId: PROJECT_ID }),
961
+ ];
962
+
963
+ await expect(
964
+ callHook(IncidentService, "onBeforeUpdate", {
965
+ data: { monitors: [{ _id: FOREIGN_MONITOR_ID }] },
966
+ query: { _id: INCIDENT_ID },
967
+ props: { isRoot: true },
968
+ }),
969
+ ).rejects.toThrow(foreignMessage("Monitor", FOREIGN_MONITOR_ID));
970
+ });
971
+
972
+ test("accepts this project's records and clearing a list", async () => {
973
+ matchedIncidents = [
974
+ storedIncident({
975
+ id: INCIDENT_ID,
976
+ projectId: PROJECT_ID,
977
+ monitorIds: [OWN_MONITOR_ID],
978
+ labelIds: [OWN_LABEL_ID],
979
+ }),
980
+ ];
981
+
982
+ await expect(
983
+ callHook(IncidentService, "onBeforeUpdate", {
984
+ data: {
985
+ monitors: [OWN_MONITOR_ID, SECOND_OWN_MONITOR_ID],
986
+ labels: [],
987
+ onCallDutyPolicies: [{ _id: OWN_POLICY_ID }],
988
+ },
989
+ query: { _id: INCIDENT_ID },
990
+ props: { tenantId: PROJECT_ID },
991
+ }),
992
+ ).resolves.toBeDefined();
993
+ });
994
+
995
+ test("an update that writes no relation list reads nothing back", async () => {
996
+ await callHook(IncidentService, "onBeforeUpdate", {
997
+ data: { title: "renamed" },
998
+ query: { _id: INCIDENT_ID },
999
+ props: { tenantId: PROJECT_ID },
1000
+ });
1001
+
1002
+ expect(IncidentService.findBy).not.toHaveBeenCalled();
1003
+ expect(MonitorService.findBy).not.toHaveBeenCalled();
1004
+ expectNoResourceLookedUp();
1005
+ });
1006
+
1007
+ test.each(RESOURCE_LISTS)(
1008
+ "rejects adding another project's $modelName",
1009
+ async (list: ResourceList) => {
1010
+ matchedIncidents = [
1011
+ storedIncident({
1012
+ id: INCIDENT_ID,
1013
+ projectId: PROJECT_ID,
1014
+ resourceIds: { [list.column]: [list.ownId] },
1015
+ }),
1016
+ ];
1017
+
1018
+ await expect(
1019
+ callHook(IncidentService, "onBeforeUpdate", {
1020
+ // Bare uuid strings, as the dashboard's resource picker saves them.
1021
+ data: { [list.column]: [list.ownId, list.foreignId] },
1022
+ query: { _id: INCIDENT_ID },
1023
+ props: { tenantId: PROJECT_ID },
1024
+ }),
1025
+ ).rejects.toThrow(foreignMessage(list.modelName, list.foreignId));
1026
+ },
1027
+ );
1028
+
1029
+ test("names every foreign affected resource in one error", async () => {
1030
+ matchedIncidents = [
1031
+ storedIncident({ id: INCIDENT_ID, projectId: PROJECT_ID }),
1032
+ ];
1033
+
1034
+ await expectEveryForeignResourceNamed(
1035
+ callHook(IncidentService, "onBeforeUpdate", {
1036
+ data: resourcesOf((list: ResourceList) => {
1037
+ return list.foreignId;
1038
+ }),
1039
+ query: { _id: INCIDENT_ID },
1040
+ props: { tenantId: PROJECT_ID },
1041
+ }),
1042
+ );
1043
+ });
1044
+
1045
+ test("re-saving a resource list keeps a resource the incident already holds", async () => {
1046
+ matchedIncidents = [
1047
+ storedIncident({
1048
+ id: INCIDENT_ID,
1049
+ projectId: PROJECT_ID,
1050
+ resourceIds: { hosts: [hostList().foreignId] },
1051
+ }),
1052
+ ];
1053
+
1054
+ await expect(
1055
+ callHook(IncidentService, "onBeforeUpdate", {
1056
+ data: {
1057
+ hosts: [hostList().foreignId.toUpperCase(), hostList().ownId],
1058
+ },
1059
+ query: { _id: INCIDENT_ID },
1060
+ props: { tenantId: PROJECT_ID },
1061
+ }),
1062
+ ).resolves.toBeDefined();
1063
+
1064
+ // Only the added host was looked up.
1065
+ expect(HostService.findBy).toHaveBeenCalledTimes(1);
1066
+ });
1067
+
1068
+ test("updates without a tenant check resources against the matched incident's project", async () => {
1069
+ matchedIncidents = [
1070
+ storedIncident({ id: INCIDENT_ID, projectId: PROJECT_ID }),
1071
+ ];
1072
+
1073
+ await expect(
1074
+ callHook(IncidentService, "onBeforeUpdate", {
1075
+ data: { services: [{ _id: RESOURCE_LISTS[13]!.foreignId }] },
1076
+ query: { _id: INCIDENT_ID },
1077
+ props: { isRoot: true },
1078
+ }),
1079
+ ).rejects.toThrow(
1080
+ foreignMessage("Service", RESOURCE_LISTS[13]!.foreignId),
1081
+ );
1082
+ });
1083
+
1084
+ test("accepts this project's resources on every list and clearing one", async () => {
1085
+ matchedIncidents = [
1086
+ storedIncident({
1087
+ id: INCIDENT_ID,
1088
+ projectId: PROJECT_ID,
1089
+ resourceIds: { hosts: [hostList().ownId] },
1090
+ }),
1091
+ ];
1092
+
1093
+ const data: Dictionary<Array<string>> = {};
1094
+
1095
+ for (const list of RESOURCE_LISTS) {
1096
+ data[list.column] = [list.ownId];
1097
+ }
1098
+
1099
+ data["hosts"] = [];
1100
+
1101
+ await expect(
1102
+ callHook(IncidentService, "onBeforeUpdate", {
1103
+ data: data,
1104
+ query: { _id: INCIDENT_ID },
1105
+ props: { tenantId: PROJECT_ID },
1106
+ }),
1107
+ ).resolves.toBeDefined();
1108
+ });
1109
+ });
1110
+
1111
+ describe("AlertService create", () => {
1112
+ let counter: jest.Mock;
1113
+
1114
+ beforeEach(() => {
1115
+ const createdState: AlertState = new AlertState();
1116
+ createdState._id = STATE_ID;
1117
+ jest
1118
+ .spyOn(AlertStateService, "findOneBy")
1119
+ .mockResolvedValue(createdState as never);
1120
+
1121
+ counter = jest.fn(async () => {
1122
+ return { counter: 1, prefix: undefined };
1123
+ }) as unknown as jest.Mock;
1124
+ jest
1125
+ .spyOn(ProjectService, "incrementAndGetAlertCounter")
1126
+ .mockImplementation(counter as never);
1127
+ });
1128
+
1129
+ function alertWith(relations: Partial<Alert>): Alert {
1130
+ const alert: Alert = new Alert();
1131
+ alert.title = "test";
1132
+ alert.alertSeverityId = new ObjectID(ALERT_SEVERITY_ID);
1133
+ Object.assign(alert, relations);
1134
+ return alert;
1135
+ }
1136
+
1137
+ test("rejects another project's on-call policy before it can be executed", async () => {
1138
+ /*
1139
+ * onCreateSuccess executes every listed policy, so this is what would
1140
+ * page the other project's on-call for this project's alert.
1141
+ */
1142
+ await expect(
1143
+ callHook(AlertService, "onBeforeCreate", {
1144
+ data: alertWith({
1145
+ onCallDutyPolicies: [stubOf(OnCallDutyPolicy, FOREIGN_POLICY_ID)],
1146
+ }),
1147
+ props: { tenantId: PROJECT_ID },
1148
+ }),
1149
+ ).rejects.toThrow(foreignMessage("On-Call Policy", FOREIGN_POLICY_ID));
1150
+
1151
+ // Rejected before the counter, so no alert number is burned.
1152
+ expect(counter).not.toHaveBeenCalled();
1153
+ });
1154
+
1155
+ test("rejects another project's label", async () => {
1156
+ await expect(
1157
+ callHook(AlertService, "onBeforeCreate", {
1158
+ data: alertWith({ labels: [stubOf(Label, FOREIGN_LABEL_ID)] }),
1159
+ props: { tenantId: PROJECT_ID },
1160
+ }),
1161
+ ).rejects.toThrow(foreignMessage("Label", FOREIGN_LABEL_ID));
1162
+ });
1163
+
1164
+ test("rejects another project's monitor by id", async () => {
1165
+ await expect(
1166
+ callHook(AlertService, "onBeforeCreate", {
1167
+ data: alertWith({ monitorId: new ObjectID(FOREIGN_MONITOR_ID) }),
1168
+ props: { tenantId: PROJECT_ID },
1169
+ }),
1170
+ ).rejects.toThrow(foreignMessage("Monitor", FOREIGN_MONITOR_ID));
1171
+
1172
+ expect(counter).not.toHaveBeenCalled();
1173
+ });
1174
+
1175
+ test("rejects another project's monitor sent as a relation", async () => {
1176
+ await expect(
1177
+ callHook(AlertService, "onBeforeCreate", {
1178
+ data: alertWith({ monitor: stubOf(Monitor, FOREIGN_MONITOR_ID) }),
1179
+ props: { tenantId: PROJECT_ID },
1180
+ }),
1181
+ ).rejects.toThrow(foreignMessage("Monitor", FOREIGN_MONITOR_ID));
1182
+ });
1183
+
1184
+ test("names every foreign record in one error", async () => {
1185
+ await expect(
1186
+ callHook(AlertService, "onBeforeCreate", {
1187
+ data: alertWith({
1188
+ monitorId: new ObjectID(FOREIGN_MONITOR_ID),
1189
+ labels: [
1190
+ stubOf(Label, OWN_LABEL_ID),
1191
+ stubOf(Label, FOREIGN_LABEL_ID),
1192
+ ],
1193
+ onCallDutyPolicies: [stubOf(OnCallDutyPolicy, FOREIGN_POLICY_ID)],
1194
+ }),
1195
+ props: { tenantId: PROJECT_ID },
1196
+ }),
1197
+ ).rejects.toThrow(
1198
+ `belong to a different project: Monitor "${nameOf(
1199
+ FOREIGN_MONITOR_ID,
1200
+ )}", Label "${nameOf(FOREIGN_LABEL_ID)}", On-Call Policy "${nameOf(
1201
+ FOREIGN_POLICY_ID,
1202
+ )}"`,
1203
+ );
1204
+ });
1205
+
1206
+ test("rejects an on-call policy id that matches no record", async () => {
1207
+ await expect(
1208
+ callHook(AlertService, "onBeforeCreate", {
1209
+ data: alertWith({
1210
+ onCallDutyPolicies: [stubOf(OnCallDutyPolicy, UNKNOWN_ID)],
1211
+ }),
1212
+ props: { tenantId: PROJECT_ID },
1213
+ }),
1214
+ ).rejects.toThrow(`do not exist: On-Call Policy "${UNKNOWN_ID}"`);
1215
+ });
1216
+
1217
+ test("root creates are checked against the project on the payload", async () => {
1218
+ // Workers create alerts as root with projectId on the data.
1219
+ const alert: Alert = alertWith({
1220
+ onCallDutyPolicies: [stubOf(OnCallDutyPolicy, FOREIGN_POLICY_ID)],
1221
+ });
1222
+ alert.projectId = PROJECT_ID;
1223
+
1224
+ await expect(
1225
+ callHook(AlertService, "onBeforeCreate", {
1226
+ data: alert,
1227
+ props: { isRoot: true },
1228
+ }),
1229
+ ).rejects.toThrow(foreignMessage("On-Call Policy", FOREIGN_POLICY_ID));
1230
+ });
1231
+
1232
+ test("accepts this project's monitor, labels and on-call policies in every shape", async () => {
1233
+ const monitor: Monitor = withProject(
1234
+ new Monitor(),
1235
+ OWN_MONITOR_ID,
1236
+ PROJECT_ID,
1237
+ );
1238
+
1239
+ const alert: Alert = alertWith({
1240
+ // A full monitor, as MonitorAlert attaches it.
1241
+ monitor: monitor,
1242
+ labels: [
1243
+ stubOf(Label, OWN_LABEL_ID),
1244
+ // A bare uuid string, as an API client may send it.
1245
+ OWN_LABEL_ID as unknown as Label,
1246
+ ],
1247
+ onCallDutyPolicies: [
1248
+ // Upper case: Postgres reads uuids back lower-cased.
1249
+ stubOf(OnCallDutyPolicy, OWN_POLICY_ID.toUpperCase()),
1250
+ { _id: OWN_POLICY_ID } as unknown as OnCallDutyPolicy,
1251
+ ],
1252
+ });
1253
+
1254
+ await expect(
1255
+ callHook(AlertService, "onBeforeCreate", {
1256
+ data: alert,
1257
+ props: { tenantId: PROJECT_ID },
1258
+ }),
1259
+ ).resolves.toBeDefined();
1260
+
1261
+ expect(counter).toHaveBeenCalledTimes(1);
1262
+ });
1263
+
1264
+ test("does not look relations up when none are sent", async () => {
1265
+ await callHook(AlertService, "onBeforeCreate", {
1266
+ data: alertWith({}),
1267
+ props: { tenantId: PROJECT_ID },
1268
+ });
1269
+
1270
+ expect(MonitorService.findBy).not.toHaveBeenCalled();
1271
+ expect(LabelService.findBy).not.toHaveBeenCalled();
1272
+ expect(OnCallDutyPolicyService.findBy).not.toHaveBeenCalled();
1273
+ expectNoResourceLookedUp();
1274
+ });
1275
+
1276
+ test.each(RESOURCE_LISTS)(
1277
+ "rejects another project's $modelName",
1278
+ async (list: ResourceList) => {
1279
+ await expect(
1280
+ callHook(AlertService, "onBeforeCreate", {
1281
+ data: alertWith({
1282
+ [list.column]: [stubOf(list.model, list.foreignId)],
1283
+ } as Partial<Alert>),
1284
+ props: { tenantId: PROJECT_ID },
1285
+ }),
1286
+ ).rejects.toThrow(foreignMessage(list.modelName, list.foreignId));
1287
+
1288
+ expect(counter).not.toHaveBeenCalled();
1289
+ },
1290
+ );
1291
+
1292
+ test("names every foreign affected resource in one error", async () => {
1293
+ await expectEveryForeignResourceNamed(
1294
+ callHook(AlertService, "onBeforeCreate", {
1295
+ data: alertWith(
1296
+ resourcesOf((list: ResourceList) => {
1297
+ return list.foreignId;
1298
+ }) as Partial<Alert>,
1299
+ ),
1300
+ props: { tenantId: PROJECT_ID },
1301
+ }),
1302
+ );
1303
+
1304
+ expect(counter).not.toHaveBeenCalled();
1305
+ });
1306
+
1307
+ test("rejects an affected resource id that matches no record", async () => {
1308
+ await expect(
1309
+ callHook(AlertService, "onBeforeCreate", {
1310
+ data: alertWith({ hosts: [stubOf(Host, UNKNOWN_ID)] }),
1311
+ props: { tenantId: PROJECT_ID },
1312
+ }),
1313
+ ).rejects.toThrow(`do not exist: Host "${UNKNOWN_ID}"`);
1314
+ });
1315
+
1316
+ test("root creates check resources against the project on the payload", async () => {
1317
+ // MonitorAlert attaches resolved resources and creates as root.
1318
+ const alert: Alert = alertWith({
1319
+ kubernetesClusters: [
1320
+ stubOf(KubernetesCluster, RESOURCE_LISTS[1]!.foreignId),
1321
+ ],
1322
+ });
1323
+ alert.projectId = PROJECT_ID;
1324
+
1325
+ await expect(
1326
+ callHook(AlertService, "onBeforeCreate", {
1327
+ data: alert,
1328
+ props: { isRoot: true },
1329
+ }),
1330
+ ).rejects.toThrow(
1331
+ foreignMessage("Kubernetes Cluster", RESOURCE_LISTS[1]!.foreignId),
1332
+ );
1333
+ });
1334
+
1335
+ test("accepts this project's record on every affected-resource list", async () => {
1336
+ await expect(
1337
+ callHook(AlertService, "onBeforeCreate", {
1338
+ data: alertWith(
1339
+ resourcesOf((list: ResourceList) => {
1340
+ return list.ownId;
1341
+ }) as Partial<Alert>,
1342
+ ),
1343
+ props: { tenantId: PROJECT_ID },
1344
+ }),
1345
+ ).resolves.toBeDefined();
1346
+
1347
+ expect(counter).toHaveBeenCalledTimes(1);
1348
+ });
1349
+ });
1350
+
1351
+ describe("AlertService update", () => {
1352
+ let matchedAlerts: Array<Alert> = [];
1353
+
1354
+ beforeEach(() => {
1355
+ matchedAlerts = [];
1356
+
1357
+ jest.spyOn(AlertService, "findBy").mockImplementation((async () => {
1358
+ return matchedAlerts;
1359
+ }) as never);
1360
+ });
1361
+
1362
+ function storedAlert(data: {
1363
+ id: string;
1364
+ projectId: ObjectID;
1365
+ labelIds?: Array<string>;
1366
+ policyIds?: Array<string>;
1367
+ resourceIds?: Dictionary<Array<string>>;
1368
+ }): Alert {
1369
+ const alert: Alert = new Alert();
1370
+ alert._id = data.id;
1371
+ alert.projectId = data.projectId;
1372
+ alert.labels = (data.labelIds || []).map((id: string) => {
1373
+ return stubOf(Label, id);
1374
+ });
1375
+ alert.onCallDutyPolicies = (data.policyIds || []).map((id: string) => {
1376
+ return stubOf(OnCallDutyPolicy, id);
1377
+ });
1378
+ for (const list of RESOURCE_LISTS) {
1379
+ Object.assign(alert, {
1380
+ [list.column]: (data.resourceIds?.[list.column] || []).map(
1381
+ (id: string) => {
1382
+ return stubOf(list.model, id);
1383
+ },
1384
+ ),
1385
+ });
1386
+ }
1387
+ return alert;
1388
+ }
1389
+
1390
+ test("rejects adding another project's label or on-call policy", async () => {
1391
+ matchedAlerts = [storedAlert({ id: ALERT_ID, projectId: PROJECT_ID })];
1392
+
1393
+ await expect(
1394
+ callHook(AlertService, "onBeforeUpdate", {
1395
+ data: {
1396
+ labels: [{ _id: FOREIGN_LABEL_ID }],
1397
+ onCallDutyPolicies: [new ObjectID(FOREIGN_POLICY_ID)],
1398
+ },
1399
+ query: { _id: ALERT_ID },
1400
+ props: { tenantId: PROJECT_ID },
1401
+ }),
1402
+ ).rejects.toThrow(
1403
+ `belong to a different project: Label "${nameOf(
1404
+ FOREIGN_LABEL_ID,
1405
+ )}", On-Call Policy "${nameOf(FOREIGN_POLICY_ID)}"`,
1406
+ );
1407
+ });
1408
+
1409
+ test("rejects repointing the alert at another project's monitor", async () => {
1410
+ matchedAlerts = [storedAlert({ id: ALERT_ID, projectId: PROJECT_ID })];
1411
+
1412
+ await expect(
1413
+ callHook(AlertService, "onBeforeUpdate", {
1414
+ data: { monitorId: FOREIGN_MONITOR_ID },
1415
+ query: { _id: ALERT_ID },
1416
+ props: { tenantId: PROJECT_ID },
1417
+ }),
1418
+ ).rejects.toThrow(foreignMessage("Monitor", FOREIGN_MONITOR_ID));
1419
+
1420
+ await expect(
1421
+ callHook(AlertService, "onBeforeUpdate", {
1422
+ // The relation slot, as a bare uuid string before sanitizing.
1423
+ data: { monitor: FOREIGN_MONITOR_ID },
1424
+ query: { _id: ALERT_ID },
1425
+ props: { tenantId: PROJECT_ID },
1426
+ }),
1427
+ ).rejects.toThrow(foreignMessage("Monitor", FOREIGN_MONITOR_ID));
1428
+ });
1429
+
1430
+ test("re-saving a list keeps an id the alert already holds", async () => {
1431
+ /*
1432
+ * Alerts created before this guard — or by a monitor whose criteria
1433
+ * still carried a stale policy — can already hold another project's
1434
+ * record. Only ids the update adds are checked.
1435
+ */
1436
+ matchedAlerts = [
1437
+ storedAlert({
1438
+ id: ALERT_ID,
1439
+ projectId: PROJECT_ID,
1440
+ policyIds: [FOREIGN_POLICY_ID],
1441
+ }),
1442
+ ];
1443
+
1444
+ await expect(
1445
+ callHook(AlertService, "onBeforeUpdate", {
1446
+ data: {
1447
+ onCallDutyPolicies: [
1448
+ FOREIGN_POLICY_ID.toUpperCase(),
1449
+ OWN_POLICY_ID,
1450
+ ],
1451
+ },
1452
+ query: { _id: ALERT_ID },
1453
+ props: { tenantId: PROJECT_ID },
1454
+ }),
1455
+ ).resolves.toBeDefined();
1456
+
1457
+ // Only the added policy was looked up.
1458
+ expect(OnCallDutyPolicyService.findBy).toHaveBeenCalledTimes(1);
1459
+ });
1460
+
1461
+ test("an id held by one matched alert is still checked for the others", async () => {
1462
+ matchedAlerts = [
1463
+ storedAlert({
1464
+ id: ALERT_ID,
1465
+ projectId: PROJECT_ID,
1466
+ labelIds: [FOREIGN_LABEL_ID],
1467
+ }),
1468
+ storedAlert({ id: SECOND_ALERT_ID, projectId: PROJECT_ID }),
1469
+ ];
1470
+
1471
+ await expect(
1472
+ callHook(AlertService, "onBeforeUpdate", {
1473
+ data: { labels: [FOREIGN_LABEL_ID] },
1474
+ query: {},
1475
+ props: { tenantId: PROJECT_ID },
1476
+ }),
1477
+ ).rejects.toThrow(foreignMessage("Label", FOREIGN_LABEL_ID));
1478
+ });
1479
+
1480
+ test("updates without a tenant are checked against each matched alert's project", async () => {
1481
+ matchedAlerts = [storedAlert({ id: ALERT_ID, projectId: PROJECT_ID })];
1482
+
1483
+ await expect(
1484
+ callHook(AlertService, "onBeforeUpdate", {
1485
+ data: { onCallDutyPolicies: [{ _id: FOREIGN_POLICY_ID }] },
1486
+ query: { _id: ALERT_ID },
1487
+ props: { isRoot: true },
1488
+ }),
1489
+ ).rejects.toThrow(foreignMessage("On-Call Policy", FOREIGN_POLICY_ID));
1490
+ });
1491
+
1492
+ test("accepts this project's records and clearing a list", async () => {
1493
+ matchedAlerts = [
1494
+ storedAlert({
1495
+ id: ALERT_ID,
1496
+ projectId: PROJECT_ID,
1497
+ labelIds: [OWN_LABEL_ID],
1498
+ }),
1499
+ ];
1500
+
1501
+ await expect(
1502
+ callHook(AlertService, "onBeforeUpdate", {
1503
+ data: {
1504
+ monitorId: OWN_MONITOR_ID,
1505
+ labels: [],
1506
+ onCallDutyPolicies: [{ _id: OWN_POLICY_ID }],
1507
+ },
1508
+ query: { _id: ALERT_ID },
1509
+ props: { tenantId: PROJECT_ID },
1510
+ }),
1511
+ ).resolves.toBeDefined();
1512
+ });
1513
+
1514
+ test("an update that writes no relation reads nothing back", async () => {
1515
+ await callHook(AlertService, "onBeforeUpdate", {
1516
+ data: { title: "renamed" },
1517
+ query: { _id: ALERT_ID },
1518
+ props: { tenantId: PROJECT_ID },
1519
+ });
1520
+
1521
+ expect(AlertService.findBy).not.toHaveBeenCalled();
1522
+ expect(MonitorService.findBy).not.toHaveBeenCalled();
1523
+ expect(LabelService.findBy).not.toHaveBeenCalled();
1524
+ expect(OnCallDutyPolicyService.findBy).not.toHaveBeenCalled();
1525
+ expectNoResourceLookedUp();
1526
+ });
1527
+
1528
+ test.each(RESOURCE_LISTS)(
1529
+ "rejects adding another project's $modelName",
1530
+ async (list: ResourceList) => {
1531
+ matchedAlerts = [
1532
+ storedAlert({
1533
+ id: ALERT_ID,
1534
+ projectId: PROJECT_ID,
1535
+ resourceIds: { [list.column]: [list.ownId] },
1536
+ }),
1537
+ ];
1538
+
1539
+ await expect(
1540
+ callHook(AlertService, "onBeforeUpdate", {
1541
+ // Bare uuid strings, as the dashboard's resource picker saves them.
1542
+ data: { [list.column]: [list.ownId, list.foreignId] },
1543
+ query: { _id: ALERT_ID },
1544
+ props: { tenantId: PROJECT_ID },
1545
+ }),
1546
+ ).rejects.toThrow(foreignMessage(list.modelName, list.foreignId));
1547
+ },
1548
+ );
1549
+
1550
+ test("names every foreign affected resource in one error", async () => {
1551
+ matchedAlerts = [storedAlert({ id: ALERT_ID, projectId: PROJECT_ID })];
1552
+
1553
+ await expectEveryForeignResourceNamed(
1554
+ callHook(AlertService, "onBeforeUpdate", {
1555
+ data: resourcesOf((list: ResourceList) => {
1556
+ return list.foreignId;
1557
+ }),
1558
+ query: { _id: ALERT_ID },
1559
+ props: { tenantId: PROJECT_ID },
1560
+ }),
1561
+ );
1562
+ });
1563
+
1564
+ test("re-saving a resource list keeps a resource the alert already holds", async () => {
1565
+ /*
1566
+ * Alerts linked before this guard can hold another project's
1567
+ * resource. The dashboard saves every list back on each edit, so
1568
+ * refusing what the alert already has would lock the user out.
1569
+ */
1570
+ matchedAlerts = [
1571
+ storedAlert({
1572
+ id: ALERT_ID,
1573
+ projectId: PROJECT_ID,
1574
+ resourceIds: { hosts: [hostList().foreignId] },
1575
+ }),
1576
+ ];
1577
+
1578
+ await expect(
1579
+ callHook(AlertService, "onBeforeUpdate", {
1580
+ data: {
1581
+ hosts: [hostList().foreignId.toUpperCase(), hostList().ownId],
1582
+ },
1583
+ query: { _id: ALERT_ID },
1584
+ props: { tenantId: PROJECT_ID },
1585
+ }),
1586
+ ).resolves.toBeDefined();
1587
+
1588
+ // Only the added host was looked up.
1589
+ expect(HostService.findBy).toHaveBeenCalledTimes(1);
1590
+ });
1591
+
1592
+ test("a resource held by one matched alert is still checked for the others", async () => {
1593
+ matchedAlerts = [
1594
+ storedAlert({
1595
+ id: ALERT_ID,
1596
+ projectId: PROJECT_ID,
1597
+ resourceIds: { hosts: [hostList().foreignId] },
1598
+ }),
1599
+ storedAlert({ id: SECOND_ALERT_ID, projectId: PROJECT_ID }),
1600
+ ];
1601
+
1602
+ await expect(
1603
+ callHook(AlertService, "onBeforeUpdate", {
1604
+ data: { hosts: [hostList().foreignId] },
1605
+ query: {},
1606
+ props: { tenantId: PROJECT_ID },
1607
+ }),
1608
+ ).rejects.toThrow(foreignMessage("Host", hostList().foreignId));
1609
+ });
1610
+
1611
+ test("updates without a tenant check resources against the matched alert's project", async () => {
1612
+ matchedAlerts = [storedAlert({ id: ALERT_ID, projectId: PROJECT_ID })];
1613
+
1614
+ await expect(
1615
+ callHook(AlertService, "onBeforeUpdate", {
1616
+ data: { services: [{ _id: RESOURCE_LISTS[13]!.foreignId }] },
1617
+ query: { _id: ALERT_ID },
1618
+ props: { isRoot: true },
1619
+ }),
1620
+ ).rejects.toThrow(
1621
+ foreignMessage("Service", RESOURCE_LISTS[13]!.foreignId),
1622
+ );
1623
+ });
1624
+
1625
+ test("accepts this project's resources on every list and clearing one", async () => {
1626
+ matchedAlerts = [
1627
+ storedAlert({
1628
+ id: ALERT_ID,
1629
+ projectId: PROJECT_ID,
1630
+ resourceIds: { hosts: [hostList().ownId] },
1631
+ }),
1632
+ ];
1633
+
1634
+ const data: Dictionary<Array<string>> = {};
1635
+
1636
+ for (const list of RESOURCE_LISTS) {
1637
+ data[list.column] = [list.ownId];
1638
+ }
1639
+
1640
+ data["hosts"] = [];
1641
+
1642
+ await expect(
1643
+ callHook(AlertService, "onBeforeUpdate", {
1644
+ data: data,
1645
+ query: { _id: ALERT_ID },
1646
+ props: { tenantId: PROJECT_ID },
1647
+ }),
1648
+ ).resolves.toBeDefined();
1649
+ });
1650
+ });
1651
+
1652
+ describe("ScheduledMaintenanceService create", () => {
1653
+ let counter: jest.Mock;
1654
+
1655
+ beforeEach(() => {
1656
+ const scheduledState: ScheduledMaintenanceState =
1657
+ new ScheduledMaintenanceState();
1658
+ scheduledState._id = STATE_ID;
1659
+ jest
1660
+ .spyOn(ScheduledMaintenanceStateService, "findOneBy")
1661
+ .mockResolvedValue(scheduledState as never);
1662
+
1663
+ counter = jest.fn(async () => {
1664
+ return { counter: 1, prefix: undefined };
1665
+ }) as unknown as jest.Mock;
1666
+ jest
1667
+ .spyOn(ProjectService, "incrementAndGetScheduledMaintenanceCounter")
1668
+ .mockImplementation(counter as never);
1669
+ });
1670
+
1671
+ function eventWith(
1672
+ relations: Partial<ScheduledMaintenance>,
1673
+ ): ScheduledMaintenance {
1674
+ const event: ScheduledMaintenance = new ScheduledMaintenance();
1675
+ event.title = "test";
1676
+ Object.assign(event, relations);
1677
+ return event;
1678
+ }
1679
+
1680
+ test("rejects another project's monitor", async () => {
1681
+ await expect(
1682
+ callHook(ScheduledMaintenanceService, "onBeforeCreate", {
1683
+ data: eventWith({ monitors: [stubOf(Monitor, FOREIGN_MONITOR_ID)] }),
1684
+ props: { tenantId: PROJECT_ID },
1685
+ }),
1686
+ ).rejects.toThrow(foreignMessage("Monitor", FOREIGN_MONITOR_ID));
1687
+
1688
+ expect(counter).not.toHaveBeenCalled();
1689
+ });
1690
+
1691
+ test("rejects another project's label", async () => {
1692
+ await expect(
1693
+ callHook(ScheduledMaintenanceService, "onBeforeCreate", {
1694
+ data: eventWith({ labels: [stubOf(Label, FOREIGN_LABEL_ID)] }),
1695
+ props: { tenantId: PROJECT_ID },
1696
+ }),
1697
+ ).rejects.toThrow(foreignMessage("Label", FOREIGN_LABEL_ID));
1698
+ });
1699
+
1700
+ test("rejects another project's status page", async () => {
1701
+ await expect(
1702
+ callHook(ScheduledMaintenanceService, "onBeforeCreate", {
1703
+ data: eventWith({
1704
+ statusPages: [stubOf(StatusPage, FOREIGN_STATUS_PAGE_ID)],
1705
+ }),
1706
+ props: { tenantId: PROJECT_ID },
1707
+ }),
1708
+ ).rejects.toThrow(foreignMessage("Status Page", FOREIGN_STATUS_PAGE_ID));
1709
+ });
1710
+
1711
+ test("root creates are checked against the project on the payload", async () => {
1712
+ const event: ScheduledMaintenance = eventWith({
1713
+ monitors: [stubOf(Monitor, FOREIGN_MONITOR_ID)],
1714
+ });
1715
+ event.projectId = PROJECT_ID;
1716
+
1717
+ await expect(
1718
+ callHook(ScheduledMaintenanceService, "onBeforeCreate", {
1719
+ data: event,
1720
+ props: { isRoot: true },
1721
+ }),
1722
+ ).rejects.toThrow(foreignMessage("Monitor", FOREIGN_MONITOR_ID));
1723
+ });
1724
+
1725
+ test("accepts this project's monitors, labels and status pages", async () => {
1726
+ await expect(
1727
+ callHook(ScheduledMaintenanceService, "onBeforeCreate", {
1728
+ data: eventWith({
1729
+ monitors: [stubOf(Monitor, OWN_MONITOR_ID)],
1730
+ labels: [stubOf(Label, OWN_LABEL_ID)],
1731
+ statusPages: [stubOf(StatusPage, OWN_STATUS_PAGE_ID)],
1732
+ }),
1733
+ props: { tenantId: PROJECT_ID },
1734
+ }),
1735
+ ).resolves.toBeDefined();
1736
+
1737
+ expect(counter).toHaveBeenCalledTimes(1);
1738
+ });
1739
+
1740
+ /*
1741
+ * The event suppresses monitoring for, and shows on the page of, every
1742
+ * resource it lists — so another project's resource here would put this
1743
+ * project's maintenance on that project's host, cluster or network site.
1744
+ */
1745
+ test.each(EVENT_RESOURCE_LISTS)(
1746
+ "rejects another project's $modelName",
1747
+ async (list: ResourceList) => {
1748
+ await expect(
1749
+ callHook(ScheduledMaintenanceService, "onBeforeCreate", {
1750
+ data: eventWith({
1751
+ [list.column]: [stubOf(list.model, list.foreignId)],
1752
+ } as Partial<ScheduledMaintenance>),
1753
+ props: { tenantId: PROJECT_ID },
1754
+ }),
1755
+ ).rejects.toThrow(foreignMessage(list.modelName, list.foreignId));
1756
+
1757
+ expect(counter).not.toHaveBeenCalled();
1758
+ },
1759
+ );
1760
+
1761
+ test("names every foreign affected resource in one error", async () => {
1762
+ await expectEveryForeignResourceNamed(
1763
+ callHook(ScheduledMaintenanceService, "onBeforeCreate", {
1764
+ data: eventWith(
1765
+ resourcesOf((list: ResourceList) => {
1766
+ return list.foreignId;
1767
+ }, EVENT_RESOURCE_LISTS) as Partial<ScheduledMaintenance>,
1768
+ ),
1769
+ props: { tenantId: PROJECT_ID },
1770
+ }),
1771
+ EVENT_RESOURCE_LISTS,
1772
+ );
1773
+
1774
+ expect(counter).not.toHaveBeenCalled();
1775
+ });
1776
+
1777
+ test("rejects an affected resource id that matches no record", async () => {
1778
+ await expect(
1779
+ callHook(ScheduledMaintenanceService, "onBeforeCreate", {
1780
+ data: eventWith({
1781
+ networkSites: [stubOf(NetworkSite, UNKNOWN_ID)],
1782
+ }),
1783
+ props: { tenantId: PROJECT_ID },
1784
+ }),
1785
+ ).rejects.toThrow(`do not exist: Network Site "${UNKNOWN_ID}"`);
1786
+ });
1787
+
1788
+ test("root creates check resources against the project on the payload", async () => {
1789
+ // The recurring-event worker creates as root with projectId on the data.
1790
+ const event: ScheduledMaintenance = eventWith({
1791
+ services: [stubOf(ServiceModel, RESOURCE_LISTS[13]!.foreignId)],
1792
+ });
1793
+ event.projectId = PROJECT_ID;
1794
+
1795
+ await expect(
1796
+ callHook(ScheduledMaintenanceService, "onBeforeCreate", {
1797
+ data: event,
1798
+ props: { isRoot: true },
1799
+ }),
1800
+ ).rejects.toThrow(
1801
+ foreignMessage("Service", RESOURCE_LISTS[13]!.foreignId),
1802
+ );
1803
+ });
1804
+
1805
+ test("accepts this project's record on every affected-resource list", async () => {
1806
+ await expect(
1807
+ callHook(ScheduledMaintenanceService, "onBeforeCreate", {
1808
+ data: eventWith(
1809
+ resourcesOf((list: ResourceList) => {
1810
+ return list.ownId;
1811
+ }, EVENT_RESOURCE_LISTS) as Partial<ScheduledMaintenance>,
1812
+ ),
1813
+ props: { tenantId: PROJECT_ID },
1814
+ }),
1815
+ ).resolves.toBeDefined();
1816
+
1817
+ expect(counter).toHaveBeenCalledTimes(1);
1818
+ });
1819
+
1820
+ test("does not look resources up when none are sent", async () => {
1821
+ await callHook(ScheduledMaintenanceService, "onBeforeCreate", {
1822
+ data: eventWith({}),
1823
+ props: { tenantId: PROJECT_ID },
1824
+ });
1825
+
1826
+ expectNoResourceLookedUp();
1827
+ });
1828
+ });
1829
+
1830
+ describe("ScheduledMaintenanceService update", () => {
1831
+ let matchedEvents: Array<ScheduledMaintenance> = [];
1832
+
1833
+ beforeEach(() => {
1834
+ matchedEvents = [];
1835
+
1836
+ jest
1837
+ .spyOn(ScheduledMaintenanceService, "findBy")
1838
+ .mockImplementation((async () => {
1839
+ return matchedEvents;
1840
+ }) as never);
1841
+ });
1842
+
1843
+ function storedEvent(data: {
1844
+ projectId: ObjectID;
1845
+ statusPageIds?: Array<string>;
1846
+ resourceIds?: Dictionary<Array<string>>;
1847
+ }): ScheduledMaintenance {
1848
+ const event: ScheduledMaintenance = new ScheduledMaintenance();
1849
+ event._id = EVENT_ID;
1850
+ event.projectId = data.projectId;
1851
+ event.monitors = [];
1852
+ event.labels = [];
1853
+ event.statusPages = (data.statusPageIds || []).map((id: string) => {
1854
+ return stubOf(StatusPage, id);
1855
+ });
1856
+ for (const list of EVENT_RESOURCE_LISTS) {
1857
+ Object.assign(event, {
1858
+ [list.column]: (data.resourceIds?.[list.column] || []).map(
1859
+ (id: string) => {
1860
+ return stubOf(list.model, id);
1861
+ },
1862
+ ),
1863
+ });
1864
+ }
1865
+ return event;
1866
+ }
1867
+
1868
+ test("rejects adding another project's monitor, label or status page", async () => {
1869
+ matchedEvents = [storedEvent({ projectId: PROJECT_ID })];
1870
+
1871
+ await expect(
1872
+ callHook(ScheduledMaintenanceService, "onBeforeUpdate", {
1873
+ data: {
1874
+ monitors: [FOREIGN_MONITOR_ID],
1875
+ labels: [{ _id: FOREIGN_LABEL_ID }],
1876
+ statusPages: [stubOf(StatusPage, FOREIGN_STATUS_PAGE_ID)],
1877
+ },
1878
+ query: { _id: EVENT_ID },
1879
+ props: { tenantId: PROJECT_ID },
1880
+ }),
1881
+ ).rejects.toThrow(
1882
+ `belong to a different project: Monitor "${nameOf(
1883
+ FOREIGN_MONITOR_ID,
1884
+ )}", Label "${nameOf(FOREIGN_LABEL_ID)}", Status Page "${nameOf(
1885
+ FOREIGN_STATUS_PAGE_ID,
1886
+ )}"`,
1887
+ );
1888
+ });
1889
+
1890
+ test("re-saving a list keeps a status page the event already holds", async () => {
1891
+ matchedEvents = [
1892
+ storedEvent({
1893
+ projectId: PROJECT_ID,
1894
+ statusPageIds: [FOREIGN_STATUS_PAGE_ID],
1895
+ }),
1896
+ ];
1897
+
1898
+ await expect(
1899
+ callHook(ScheduledMaintenanceService, "onBeforeUpdate", {
1900
+ data: {
1901
+ statusPages: [FOREIGN_STATUS_PAGE_ID, OWN_STATUS_PAGE_ID],
1902
+ },
1903
+ query: { _id: EVENT_ID },
1904
+ props: { tenantId: PROJECT_ID },
1905
+ }),
1906
+ ).resolves.toBeDefined();
1907
+ });
1908
+
1909
+ test("updates without a tenant are checked against the matched event's project", async () => {
1910
+ matchedEvents = [storedEvent({ projectId: PROJECT_ID })];
1911
+
1912
+ await expect(
1913
+ callHook(ScheduledMaintenanceService, "onBeforeUpdate", {
1914
+ data: { statusPages: [FOREIGN_STATUS_PAGE_ID] },
1915
+ query: { _id: EVENT_ID },
1916
+ props: { isRoot: true },
1917
+ }),
1918
+ ).rejects.toThrow(foreignMessage("Status Page", FOREIGN_STATUS_PAGE_ID));
1919
+ });
1920
+
1921
+ test.each(EVENT_RESOURCE_LISTS)(
1922
+ "rejects adding another project's $modelName",
1923
+ async (list: ResourceList) => {
1924
+ matchedEvents = [
1925
+ storedEvent({
1926
+ projectId: PROJECT_ID,
1927
+ resourceIds: { [list.column]: [list.ownId] },
1928
+ }),
1929
+ ];
1930
+
1931
+ await expect(
1932
+ callHook(ScheduledMaintenanceService, "onBeforeUpdate", {
1933
+ // Bare uuid strings, as the dashboard's resource picker saves them.
1934
+ data: { [list.column]: [list.ownId, list.foreignId] },
1935
+ query: { _id: EVENT_ID },
1936
+ props: { tenantId: PROJECT_ID },
1937
+ }),
1938
+ ).rejects.toThrow(foreignMessage(list.modelName, list.foreignId));
1939
+ },
1940
+ );
1941
+
1942
+ test("names every foreign affected resource in one error", async () => {
1943
+ matchedEvents = [storedEvent({ projectId: PROJECT_ID })];
1944
+
1945
+ await expectEveryForeignResourceNamed(
1946
+ callHook(ScheduledMaintenanceService, "onBeforeUpdate", {
1947
+ data: resourcesOf((list: ResourceList) => {
1948
+ return list.foreignId;
1949
+ }, EVENT_RESOURCE_LISTS),
1950
+ query: { _id: EVENT_ID },
1951
+ props: { tenantId: PROJECT_ID },
1952
+ }),
1953
+ EVENT_RESOURCE_LISTS,
1954
+ );
1955
+ });
1956
+
1957
+ test("re-saving a resource list keeps a resource the event already holds", async () => {
1958
+ matchedEvents = [
1959
+ storedEvent({
1960
+ projectId: PROJECT_ID,
1961
+ resourceIds: { networkSites: [NETWORK_SITE_LIST.foreignId] },
1962
+ }),
1963
+ ];
1964
+
1965
+ await expect(
1966
+ callHook(ScheduledMaintenanceService, "onBeforeUpdate", {
1967
+ data: {
1968
+ networkSites: [
1969
+ NETWORK_SITE_LIST.foreignId.toUpperCase(),
1970
+ NETWORK_SITE_LIST.ownId,
1971
+ ],
1972
+ },
1973
+ query: { _id: EVENT_ID },
1974
+ props: { tenantId: PROJECT_ID },
1975
+ }),
1976
+ ).resolves.toBeDefined();
1977
+
1978
+ // Only the added network site was looked up.
1979
+ expect(NetworkSiteService.findBy).toHaveBeenCalledTimes(1);
1980
+ });
1981
+
1982
+ test("updates without a tenant check resources against the matched event's project", async () => {
1983
+ matchedEvents = [storedEvent({ projectId: PROJECT_ID })];
1984
+
1985
+ await expect(
1986
+ callHook(ScheduledMaintenanceService, "onBeforeUpdate", {
1987
+ data: { hosts: [{ _id: hostList().foreignId }] },
1988
+ query: { _id: EVENT_ID },
1989
+ props: { isRoot: true },
1990
+ }),
1991
+ ).rejects.toThrow(foreignMessage("Host", hostList().foreignId));
1992
+ });
1993
+
1994
+ test("accepts this project's resources on every list and clearing one", async () => {
1995
+ matchedEvents = [
1996
+ storedEvent({
1997
+ projectId: PROJECT_ID,
1998
+ resourceIds: { hosts: [hostList().ownId] },
1999
+ }),
2000
+ ];
2001
+
2002
+ const data: Dictionary<Array<string>> = {};
2003
+
2004
+ for (const list of EVENT_RESOURCE_LISTS) {
2005
+ data[list.column] = [list.ownId];
2006
+ }
2007
+
2008
+ data["hosts"] = [];
2009
+
2010
+ await expect(
2011
+ callHook(ScheduledMaintenanceService, "onBeforeUpdate", {
2012
+ data: data,
2013
+ query: { _id: EVENT_ID },
2014
+ props: { tenantId: PROJECT_ID },
2015
+ }),
2016
+ ).resolves.toBeDefined();
2017
+ });
2018
+
2019
+ test("an update that writes no relation list reads nothing back", async () => {
2020
+ await callHook(ScheduledMaintenanceService, "onBeforeUpdate", {
2021
+ data: { title: "renamed" },
2022
+ query: { _id: EVENT_ID },
2023
+ props: { tenantId: PROJECT_ID },
2024
+ });
2025
+
2026
+ expect(ScheduledMaintenanceService.findBy).not.toHaveBeenCalled();
2027
+ expectNoResourceLookedUp();
2028
+ });
2029
+ });
2030
+
2031
+ /*
2032
+ * A template's lists are copied onto every incident or event created from
2033
+ * it, and checked there — so a foreign id on the template made every
2034
+ * create from it fail. The template refuses it where it enters instead.
2035
+ */
2036
+ interface TemplateCase {
2037
+ name: string;
2038
+ subject: string;
2039
+ service: DatabaseService<DatabaseBaseModel>;
2040
+ model: new () => DatabaseBaseModel;
2041
+ lists: Array<ResourceList>;
2042
+ }
2043
+
2044
+ const TEMPLATE_CASES: Array<TemplateCase> = [
2045
+ {
2046
+ name: "IncidentTemplateService",
2047
+ subject: "incident template",
2048
+ service:
2049
+ IncidentTemplateService as unknown as DatabaseService<DatabaseBaseModel>,
2050
+ model: IncidentTemplate,
2051
+ lists: INCIDENT_TEMPLATE_LISTS,
2052
+ },
2053
+ {
2054
+ name: "ScheduledMaintenanceTemplateService",
2055
+ subject: "scheduled maintenance template",
2056
+ service:
2057
+ ScheduledMaintenanceTemplateService as unknown as DatabaseService<DatabaseBaseModel>,
2058
+ model: ScheduledMaintenanceTemplate,
2059
+ lists: EVENT_TEMPLATE_LISTS,
2060
+ },
2061
+ ];
2062
+
2063
+ describe.each(TEMPLATE_CASES)("$name", (templateCase: TemplateCase) => {
2064
+ function templateWith(lists: Dictionary<unknown>): DatabaseBaseModel {
2065
+ const template: DatabaseBaseModel = new templateCase.model();
2066
+ template.setValue("templateName", "test");
2067
+ Object.assign(template, lists);
2068
+ return template;
2069
+ }
2070
+
2071
+ function everyList(
2072
+ pick: (list: ResourceList) => string,
2073
+ ): Dictionary<Array<DatabaseBaseModel>> {
2074
+ return resourcesOf(pick, templateCase.lists);
2075
+ }
2076
+
2077
+ describe("create", () => {
2078
+ test.each(templateCase.lists)(
2079
+ "rejects another project's $modelName",
2080
+ async (list: ResourceList) => {
2081
+ await expect(
2082
+ callHook(templateCase.service, "onBeforeCreate", {
2083
+ data: templateWith({
2084
+ [list.column]: [stubOf(list.model, list.foreignId)],
2085
+ }),
2086
+ props: { tenantId: PROJECT_ID },
2087
+ }),
2088
+ ).rejects.toThrow(foreignMessage(list.modelName, list.foreignId));
2089
+ },
2090
+ );
2091
+
2092
+ test("names every foreign record in one error", async () => {
2093
+ let message: string = "";
2094
+
2095
+ try {
2096
+ await callHook(templateCase.service, "onBeforeCreate", {
2097
+ data: templateWith(
2098
+ everyList((list: ResourceList) => {
2099
+ return list.foreignId;
2100
+ }),
2101
+ ),
2102
+ props: { tenantId: PROJECT_ID },
2103
+ });
2104
+ } catch (err) {
2105
+ message = (err as Error).message;
2106
+ }
2107
+
2108
+ expect(message).toContain(
2109
+ `This ${templateCase.subject} references records that belong to a different project: `,
2110
+ );
2111
+
2112
+ for (const list of templateCase.lists) {
2113
+ expect(message).toContain(
2114
+ `${list.modelName} "${nameOf(list.foreignId)}"`,
2115
+ );
2116
+ }
2117
+ });
2118
+
2119
+ test("rejects an id that matches no record", async () => {
2120
+ await expect(
2121
+ callHook(templateCase.service, "onBeforeCreate", {
2122
+ data: templateWith({ hosts: [stubOf(Host, UNKNOWN_ID)] }),
2123
+ props: { tenantId: PROJECT_ID },
2124
+ }),
2125
+ ).rejects.toThrow(`do not exist: Host "${UNKNOWN_ID}"`);
2126
+ });
2127
+
2128
+ test("root creates are checked against the project on the payload", async () => {
2129
+ const template: DatabaseBaseModel = templateWith({
2130
+ monitors: [stubOf(Monitor, FOREIGN_MONITOR_ID)],
2131
+ });
2132
+ template.setValue("projectId", PROJECT_ID);
2133
+
2134
+ await expect(
2135
+ callHook(templateCase.service, "onBeforeCreate", {
2136
+ data: template,
2137
+ props: { isRoot: true },
2138
+ }),
2139
+ ).rejects.toThrow(foreignMessage("Monitor", FOREIGN_MONITOR_ID));
2140
+ });
2141
+
2142
+ test("accepts this project's record on every list", async () => {
2143
+ await expect(
2144
+ callHook(templateCase.service, "onBeforeCreate", {
2145
+ data: templateWith(
2146
+ everyList((list: ResourceList) => {
2147
+ return list.ownId;
2148
+ }),
2149
+ ),
2150
+ props: { tenantId: PROJECT_ID },
2151
+ }),
2152
+ ).resolves.toBeDefined();
2153
+ });
2154
+
2155
+ test("does not look relations up when none are sent", async () => {
2156
+ await callHook(templateCase.service, "onBeforeCreate", {
2157
+ data: templateWith({}),
2158
+ props: { tenantId: PROJECT_ID },
2159
+ });
2160
+
2161
+ expect(MonitorService.findBy).not.toHaveBeenCalled();
2162
+ expect(LabelService.findBy).not.toHaveBeenCalled();
2163
+ expectNoResourceLookedUp();
2164
+ });
2165
+ });
2166
+
2167
+ describe("update", () => {
2168
+ let matchedTemplates: Array<DatabaseBaseModel> = [];
2169
+
2170
+ beforeEach(() => {
2171
+ matchedTemplates = [];
2172
+
2173
+ jest
2174
+ .spyOn(templateCase.service, "findBy")
2175
+ .mockImplementation((async () => {
2176
+ return matchedTemplates;
2177
+ }) as never);
2178
+ });
2179
+
2180
+ function storedTemplate(data: {
2181
+ id: string;
2182
+ projectId: ObjectID;
2183
+ ids?: Dictionary<Array<string>>;
2184
+ }): DatabaseBaseModel {
2185
+ const template: DatabaseBaseModel = new templateCase.model();
2186
+ template._id = data.id;
2187
+ template.setValue("projectId", data.projectId);
2188
+
2189
+ for (const list of templateCase.lists) {
2190
+ Object.assign(template, {
2191
+ [list.column]: (data.ids?.[list.column] || []).map((id: string) => {
2192
+ return stubOf(list.model, id);
2193
+ }),
2194
+ });
2195
+ }
2196
+
2197
+ return template;
2198
+ }
2199
+
2200
+ test.each(templateCase.lists)(
2201
+ "rejects adding another project's $modelName",
2202
+ async (list: ResourceList) => {
2203
+ matchedTemplates = [
2204
+ storedTemplate({
2205
+ id: TEMPLATE_ID,
2206
+ projectId: PROJECT_ID,
2207
+ ids: { [list.column]: [list.ownId] },
2208
+ }),
2209
+ ];
2210
+
2211
+ await expect(
2212
+ callHook(templateCase.service, "onBeforeUpdate", {
2213
+ data: { [list.column]: [list.ownId, list.foreignId] },
2214
+ query: { _id: TEMPLATE_ID },
2215
+ props: { tenantId: PROJECT_ID },
2216
+ }),
2217
+ ).rejects.toThrow(foreignMessage(list.modelName, list.foreignId));
2218
+ },
2219
+ );
2220
+
2221
+ test("re-saving a list keeps an id the template already holds", async () => {
2222
+ /*
2223
+ * Templates saved before their lists were checked can hold another
2224
+ * project's record. Refusing to save back the list they already
2225
+ * have would lock the user out of editing the template.
2226
+ */
2227
+ matchedTemplates = [
2228
+ storedTemplate({
2229
+ id: TEMPLATE_ID,
2230
+ projectId: PROJECT_ID,
2231
+ ids: { hosts: [hostList().foreignId] },
2232
+ }),
2233
+ ];
2234
+
2235
+ await expect(
2236
+ callHook(templateCase.service, "onBeforeUpdate", {
2237
+ data: {
2238
+ hosts: [hostList().foreignId.toUpperCase(), hostList().ownId],
2239
+ },
2240
+ query: { _id: TEMPLATE_ID },
2241
+ props: { tenantId: PROJECT_ID },
2242
+ }),
2243
+ ).resolves.toBeDefined();
2244
+
2245
+ // Only the added host was looked up.
2246
+ expect(HostService.findBy).toHaveBeenCalledTimes(1);
2247
+ });
2248
+
2249
+ test("an id held by one matched template is still checked for the others", async () => {
2250
+ matchedTemplates = [
2251
+ storedTemplate({
2252
+ id: TEMPLATE_ID,
2253
+ projectId: PROJECT_ID,
2254
+ ids: { hosts: [hostList().foreignId] },
2255
+ }),
2256
+ storedTemplate({ id: SECOND_TEMPLATE_ID, projectId: PROJECT_ID }),
2257
+ ];
2258
+
2259
+ await expect(
2260
+ callHook(templateCase.service, "onBeforeUpdate", {
2261
+ data: { hosts: [hostList().foreignId] },
2262
+ query: { projectId: PROJECT_ID },
2263
+ props: { tenantId: PROJECT_ID },
2264
+ }),
2265
+ ).rejects.toThrow(foreignMessage("Host", hostList().foreignId));
2266
+ });
2267
+
2268
+ test("updates without a tenant are checked against the matched template's project", async () => {
2269
+ matchedTemplates = [
2270
+ storedTemplate({ id: TEMPLATE_ID, projectId: PROJECT_ID }),
2271
+ ];
2272
+
2273
+ await expect(
2274
+ callHook(templateCase.service, "onBeforeUpdate", {
2275
+ data: { services: [{ _id: RESOURCE_LISTS[13]!.foreignId }] },
2276
+ query: { _id: TEMPLATE_ID },
2277
+ props: { isRoot: true },
2278
+ }),
2279
+ ).rejects.toThrow(
2280
+ foreignMessage("Service", RESOURCE_LISTS[13]!.foreignId),
2281
+ );
2282
+ });
2283
+
2284
+ test("accepts this project's records on every list and clearing one", async () => {
2285
+ matchedTemplates = [
2286
+ storedTemplate({
2287
+ id: TEMPLATE_ID,
2288
+ projectId: PROJECT_ID,
2289
+ ids: { hosts: [hostList().ownId] },
2290
+ }),
2291
+ ];
2292
+
2293
+ const data: Dictionary<Array<string>> = {};
2294
+
2295
+ for (const list of templateCase.lists) {
2296
+ data[list.column] = [list.ownId];
2297
+ }
2298
+
2299
+ data["hosts"] = [];
2300
+
2301
+ await expect(
2302
+ callHook(templateCase.service, "onBeforeUpdate", {
2303
+ data: data,
2304
+ query: { _id: TEMPLATE_ID },
2305
+ props: { tenantId: PROJECT_ID },
2306
+ }),
2307
+ ).resolves.toBeDefined();
2308
+ });
2309
+
2310
+ test("an update that writes no relation list looks nothing up", async () => {
2311
+ matchedTemplates = [
2312
+ storedTemplate({ id: TEMPLATE_ID, projectId: PROJECT_ID }),
2313
+ ];
2314
+
2315
+ await callHook(templateCase.service, "onBeforeUpdate", {
2316
+ data: { templateName: "renamed" },
2317
+ query: { _id: TEMPLATE_ID },
2318
+ props: { tenantId: PROJECT_ID },
2319
+ });
2320
+
2321
+ expect(MonitorService.findBy).not.toHaveBeenCalled();
2322
+ expect(LabelService.findBy).not.toHaveBeenCalled();
2323
+ expectNoResourceLookedUp();
2324
+ });
2325
+ });
2326
+ });
2327
+
2328
+ /*
2329
+ * The gap these tests close was a list added to a model without being
2330
+ * added to its service's check. Every many-to-many list on these models
2331
+ * that points at a project's records must be checked, against the table
2332
+ * the list actually points at.
2333
+ */
2334
+ describe("every many-to-many list of project records is checked", () => {
2335
+ test.each([
2336
+ {
2337
+ name: "ScheduledMaintenanceService",
2338
+ service: ScheduledMaintenanceService,
2339
+ },
2340
+ { name: "IncidentTemplateService", service: IncidentTemplateService },
2341
+ {
2342
+ name: "ScheduledMaintenanceTemplateService",
2343
+ service: ScheduledMaintenanceTemplateService,
2344
+ },
2345
+ ])("$name", ({ service }: { service: unknown }) => {
2346
+ const model: DatabaseBaseModel = (
2347
+ service as DatabaseService<DatabaseBaseModel>
2348
+ ).getModel();
2349
+
2350
+ const projectScopedLists: Array<string> = model
2351
+ .getTableColumns()
2352
+ .columns.filter((column: string) => {
2353
+ const metadata: TableColumnMetadata =
2354
+ model.getTableColumnMetadata(column);
2355
+
2356
+ return (
2357
+ metadata.type === TableColumnType.EntityArray &&
2358
+ Boolean(metadata.modelType) &&
2359
+ Boolean(new metadata.modelType!().getTenantColumn())
2360
+ );
2361
+ });
2362
+
2363
+ const relations: Array<ProjectScopedRelation> = (
2364
+ service as {
2365
+ getProjectScopedRelations: () => Array<ProjectScopedRelation>;
2366
+ }
2367
+ ).getProjectScopedRelations();
2368
+
2369
+ expect(
2370
+ relations
2371
+ .map((relation: ProjectScopedRelation) => {
2372
+ return relation.column;
2373
+ })
2374
+ .sort(),
2375
+ ).toEqual([...projectScopedLists].sort());
2376
+
2377
+ for (const relation of relations) {
2378
+ expect(relation.service.getModel()).toBeInstanceOf(
2379
+ model.getTableColumnMetadata(relation.column).modelType!,
2380
+ );
2381
+ }
2382
+ });
2383
+ });
2384
+ });