@oneuptime/common 13.0.7 → 13.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (463) hide show
  1. package/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +1 -1
  2. package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +1 -1
  3. package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +1 -1
  4. package/Server/API/AIBillingAPI.ts +1 -0
  5. package/Server/API/AIInsightAPI.ts +1 -4
  6. package/Server/API/AIInvestigationAPI.ts +1 -4
  7. package/Server/API/AIReadinessAPI.ts +4 -5
  8. package/Server/API/AlertAPI.ts +1 -0
  9. package/Server/API/AutoRemediationAPI.ts +1 -4
  10. package/Server/API/BaseAPI.ts +13 -0
  11. package/Server/API/BaseAnalyticsAPI.ts +80 -9
  12. package/Server/API/BillingAPI.ts +2 -0
  13. package/Server/API/BillingInvoiceAPI.ts +1 -0
  14. package/Server/API/BillingPaymentMethodAPI.ts +2 -0
  15. package/Server/API/CodeFixRunAPI.ts +1 -3
  16. package/Server/API/CommonAPI.ts +68 -4
  17. package/Server/API/DashboardAPI.ts +12 -12
  18. package/Server/API/GlobalConfigAPI.ts +17 -0
  19. package/Server/API/IncidentAPI.ts +2 -0
  20. package/Server/API/IncidentEpisodeAPI.ts +1 -0
  21. package/Server/API/NotificationAPI.ts +1 -0
  22. package/Server/API/OnCallCalendarAPI.ts +4 -5
  23. package/Server/API/OnCallDutyPolicyAPI.ts +7 -0
  24. package/Server/API/OnCallReadinessAPI.ts +4 -1
  25. package/Server/API/OnCallScheduleTimelineAPI.ts +731 -0
  26. package/Server/API/ProjectAPI.ts +1 -0
  27. package/Server/API/ScheduledMaintenanceAPI.ts +1 -0
  28. package/Server/API/StatusPageAPI.ts +161 -32
  29. package/Server/API/TeamMemberAPI.ts +3 -0
  30. package/Server/API/UserCallAPI.ts +2 -0
  31. package/Server/API/UserEmailAPI.ts +2 -0
  32. package/Server/API/UserIncomingCallNumberAPI.ts +2 -0
  33. package/Server/API/UserMicrosoftTeamsAPI.ts +1 -0
  34. package/Server/API/UserSlackAPI.ts +1 -0
  35. package/Server/API/UserSmsAPI.ts +2 -0
  36. package/Server/API/UserTotpAuthAPI.ts +1 -0
  37. package/Server/API/UserWebAuthnAPI.ts +2 -0
  38. package/Server/API/UserWebhookAPI.ts +1 -0
  39. package/Server/API/UserWhatsAppAPI.ts +2 -0
  40. package/Server/Middleware/MasterAdminAuthorization.ts +13 -2
  41. package/Server/Middleware/UserAuthorization.ts +79 -5
  42. package/Server/Services/ApiKeyPermissionService.ts +101 -4
  43. package/Server/Services/DatabaseService.ts +245 -0
  44. package/Server/Services/DetectionRuleService.ts +5 -4
  45. package/Server/Services/OnCallDutyPolicyChildService.ts +261 -0
  46. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -2
  47. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +2 -2
  48. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -2
  49. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -2
  50. package/Server/Services/PayAsYouGoBillingService.ts +23 -28
  51. package/Server/Services/ProjectService.ts +18 -0
  52. package/Server/Services/RumSessionPinService.ts +274 -22
  53. package/Server/Services/TeamPermissionService.ts +120 -26
  54. package/Server/Services/ThreatIntelFeedService.ts +6 -3
  55. package/Server/Types/Database/Permissions/BasePermission.ts +9 -7
  56. package/Server/Types/Database/Permissions/Index.ts +75 -11
  57. package/Server/Types/Database/Permissions/TablePermission.ts +24 -28
  58. package/Server/Utils/AI/AIChatPrivacyFilter.ts +8 -0
  59. package/Server/Utils/AI/AIRunPrivacyFilter.ts +8 -0
  60. package/Server/Utils/MasterAdminPageAccess.ts +61 -0
  61. package/Server/Utils/Monitor/MonitorAlert.ts +3 -1
  62. package/Server/Utils/Monitor/MonitorIncident.ts +3 -1
  63. package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1 -1
  64. package/Server/Utils/Realtime.ts +163 -45
  65. package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +11 -28
  66. package/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.ts +170 -0
  67. package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +1 -1
  68. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.ts +6 -2
  69. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.ts +10 -9
  70. package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +1059 -0
  71. package/Tests/App/Dashboard/AffectedResourcesPickerEditForm.test.tsx +376 -0
  72. package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +757 -0
  73. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +307 -0
  74. package/Tests/App/Dashboard/ArgumentsFormGroupByUnlistedAttributes.test.tsx +131 -0
  75. package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +1 -1
  76. package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +9 -1
  77. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +24 -0
  78. package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +162 -8
  79. package/Tests/App/Dashboard/EntityFilterDropdownArchivedSlos.test.tsx +105 -6
  80. package/Tests/App/Dashboard/EntityFilterDropdownUnlistedSelection.test.tsx +434 -0
  81. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +276 -1
  82. package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +9 -1
  83. package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +58 -42
  84. package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +42 -5
  85. package/Tests/App/Dashboard/ResourceAuditLogsSideMenus.test.tsx +34 -23
  86. package/Tests/App/Dashboard/ScheduleTimeline.test.tsx +1580 -0
  87. package/Tests/App/Dashboard/ScheduleTimelineAPI.test.ts +280 -0
  88. package/Tests/App/Dashboard/ScheduleTimelineModel.test.ts +673 -0
  89. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +95 -68
  90. package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +86 -3
  91. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +28 -3
  92. package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +761 -0
  93. package/Tests/App/Dashboard/SecurityEventsGuideContent.test.ts +1313 -0
  94. package/Tests/App/Dashboard/SecurityEventsHowItWorksCard.test.tsx +536 -0
  95. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +222 -0
  96. package/Tests/App/Dashboard/SecurityEventsThreatIntelPage.test.tsx +199 -0
  97. package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +321 -0
  98. package/Tests/App/Dashboard/SloOverviewHero.test.tsx +171 -36
  99. package/Tests/App/Dashboard/SloViewSideMenu.test.tsx +215 -0
  100. package/Tests/App/Dashboard/SloViewSideMenuCounts.test.tsx +40 -3
  101. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +12 -4
  102. package/Tests/App/PublicDashboard/PublicDashboardWidgetListAuth.test.tsx +623 -0
  103. package/Tests/Models/OnCallCalendarFeedModels.test.ts +23 -1
  104. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +138 -5
  105. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +187 -0
  106. package/Tests/Server/API/AIInvestigationAvailabilityAuthorization.test.ts +51 -3
  107. package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +55 -2
  108. package/Tests/Server/API/BaseAnalyticsAPIAggregateCache.test.ts +729 -0
  109. package/Tests/Server/API/BillingPayAsYouGoStatus.test.ts +93 -7
  110. package/Tests/Server/API/BillingPaymentMethodAPI.test.ts +36 -3
  111. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +1003 -24
  112. package/Tests/Server/API/DashboardPublicRateLimit.test.ts +6 -3
  113. package/Tests/Server/API/ExpiredSessionAIRoutes.test.ts +708 -0
  114. package/Tests/Server/API/ExpiredSessionCustomRoutes.test.ts +928 -0
  115. package/Tests/Server/API/ExpiredSessionMonitorTemplate.test.ts +319 -0
  116. package/Tests/Server/API/ExpiredSessionOnCallDutyPolicy.test.ts +336 -0
  117. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +820 -0
  118. package/Tests/Server/API/GlobalConfigLicense.test.ts +140 -0
  119. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +115 -12
  120. package/Tests/Server/API/OnCallCalendarAPI.test.ts +375 -8
  121. package/Tests/Server/API/OnCallReadinessAPI.test.ts +175 -17
  122. package/Tests/Server/API/OnCallScheduleTimelineAPI.test.ts +1513 -0
  123. package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +119 -7
  124. package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +111 -4
  125. package/Tests/Server/API/PublicRouteInvalidAccessToken.test.ts +615 -0
  126. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +89 -2
  127. package/Tests/Server/API/SessionReplayAPI.test.ts +121 -0
  128. package/Tests/Server/API/StatusPageMasterPasswordRateLimit.test.ts +4 -2
  129. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +441 -0
  130. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +664 -0
  131. package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +7 -0
  132. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +6 -0
  133. package/Tests/Server/API/UserNotificationSettingAPI.test.ts +65 -4
  134. package/Tests/Server/API/UserProjectsAPI.test.ts +63 -3
  135. package/Tests/Server/API/UserTotpAuthAPI.test.ts +48 -2
  136. package/Tests/Server/API/UserWebAuthnAuthenticationOptionsRoute.test.ts +11 -2
  137. package/Tests/Server/API/UserWebAuthnBackupCodeMinting.test.ts +46 -3
  138. package/Tests/Server/API/UserWebAuthnPasskeyAPI.test.ts +18 -2
  139. package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +24 -7
  140. package/Tests/Server/Middleware/ExpiredSessionUserMiddleware.test.ts +563 -0
  141. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +299 -19
  142. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +453 -0
  143. package/Tests/Server/Services/AIChatPrivacyPin.test.ts +24 -0
  144. package/Tests/Server/Services/ApiKeyPermissionSecurity.test.ts +2 -0
  145. package/Tests/Server/Services/ApiKeyPermissionService.test.ts +3 -3
  146. package/Tests/Server/Services/DatabaseServiceCreateSuppliedId.test.ts +147 -0
  147. package/Tests/Server/Services/DatabaseServiceLoginBeforeHooks.test.ts +1078 -0
  148. package/Tests/Server/Services/DatabaseServiceTenantRelationGuard.test.ts +259 -0
  149. package/Tests/Server/Services/DatabaseServiceWriteOperatorValues.test.ts +147 -0
  150. package/Tests/Server/Services/MonitorPayAsYouGoBilling.test.ts +115 -1
  151. package/Tests/Server/Services/OnCallChildCreateAuthorizationPostgres.test.ts +711 -0
  152. package/Tests/Server/Services/OnCallDutyPolicyChildService.test.ts +733 -0
  153. package/Tests/Server/Services/PayAsYouGoBillingService.test.ts +190 -140
  154. package/Tests/Server/Services/PayAsYouGoStripeRateLimitBudget.test.ts +4 -9
  155. package/Tests/Server/Services/PermissionBlockDeletion.test.ts +780 -0
  156. package/Tests/Server/Services/PermissionBlockDeletionPostgres.test.ts +628 -0
  157. package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +77 -1
  158. package/Tests/Server/Services/RumSessionPinCreatePermission.test.ts +715 -0
  159. package/Tests/Server/Services/TeamMemberMultiTenantReadPostgres.test.ts +394 -0
  160. package/Tests/Server/Services/TeamPrivilegeEscalation.test.ts +6 -0
  161. package/Tests/Server/Services/TelemetryPayAsYouGoBilling.test.ts +131 -0
  162. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +13 -2
  163. package/Tests/Server/Types/Database/Permissions/ModelPermissionAnonymousRefusal.test.ts +518 -0
  164. package/Tests/Server/Types/Database/Permissions/MultiTenantPermissions.test.ts +350 -0
  165. package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +282 -25
  166. package/Tests/Server/Types/Database/Permissions/TableLevelBlockPermission.test.ts +459 -0
  167. package/Tests/Server/Utils/AI/AIRunPrivacyFilter.test.ts +101 -9
  168. package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +88 -4
  169. package/Tests/Server/Utils/AI/Toolbox/ExceptionTools.test.ts +459 -0
  170. package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +794 -0
  171. package/Tests/Server/Utils/AI/Toolbox/RecentChangesTools.test.ts +574 -0
  172. package/Tests/Server/Utils/AI/Toolbox/SecurityEventTools.test.ts +596 -0
  173. package/Tests/Server/Utils/CaptchaRequestAndLogging.test.ts +395 -0
  174. package/Tests/Server/Utils/MasterAdminPageAccess.test.ts +251 -0
  175. package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +616 -0
  176. package/Tests/Server/Utils/Realtime.test.ts +502 -0
  177. package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorSeverity.test.ts +216 -0
  178. package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +142 -0
  179. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.test.ts +140 -0
  180. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepairClickhouseIntegration.test.ts +542 -0
  181. package/Tests/Server/Utils/Telemetry/ChangeEventRow.test.ts +743 -0
  182. package/Tests/Server/Utils/Telemetry/RuntimeMetrics.test.ts +465 -0
  183. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.test.ts +202 -0
  184. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.test.ts +194 -0
  185. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.test.ts +231 -0
  186. package/Tests/Server/Utils/Workspace/Slack/Messages/Alert.test.ts +194 -0
  187. package/Tests/Server/Utils/Workspace/Slack/Messages/AlertEpisode.test.ts +194 -0
  188. package/Tests/Server/Utils/Workspace/Slack/Messages/Incident.test.ts +194 -0
  189. package/Tests/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.test.ts +194 -0
  190. package/Tests/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.test.ts +247 -0
  191. package/Tests/Types/AI/AIInsightEvidence.test.ts +206 -0
  192. package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtilAnonymous.test.ts +144 -0
  193. package/Tests/Types/Call/CallProvider.test.ts +528 -0
  194. package/Tests/Types/Kubernetes/KubernetesCostIngest.test.ts +236 -0
  195. package/Tests/Types/OnCallDutyPolicy/ScheduleTimeline.test.ts +736 -0
  196. package/Tests/Types/OnCallDutyPolicy/ScheduleTimelineLayout.test.ts +996 -0
  197. package/Tests/Types/SecurityEvent/SigmaRule.test.ts +142 -0
  198. package/Tests/Types/Workflow/Components/JSON.test.ts +182 -0
  199. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +116 -1
  200. package/Tests/UI/Components/Dropdown/DropdownAsyncOptions.test.tsx +81 -0
  201. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +89 -1
  202. package/Tests/UI/Components/EventAttachmentList.test.tsx +174 -0
  203. package/Tests/UI/Components/Forms/BasicFormBurnRateTemplateDefaults.test.tsx +395 -0
  204. package/Tests/UI/Components/Forms/BasicFormCollapsibleSections.test.tsx +286 -0
  205. package/Tests/UI/Components/Forms/FormSummary.test.tsx +128 -0
  206. package/Tests/UI/Components/ModelTable/BaseModelTableQueryChange.test.tsx +365 -0
  207. package/Tests/UI/Components/OpenAuthenticatedUrl.test.ts +358 -0
  208. package/Tests/UI/Components/SessionAwareImage.test.tsx +496 -0
  209. package/Tests/UI/Components/TelemetryHistogramZeroBuckets.test.tsx +190 -0
  210. package/Tests/UI/Components/TooltipInteractive.test.tsx +97 -0
  211. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardInstallGuidance.test.tsx +1 -1
  212. package/Tests/UI/Monitor/EvaluationLogListTimestamps.test.tsx +367 -0
  213. package/Tests/UI/Utils/API/BaseAPIRefreshSession.test.ts +1087 -0
  214. package/Tests/UI/Utils/API/DashboardHost.ts +12 -0
  215. package/Tests/UI/Utils/API/FakeAxiosServer.ts +183 -0
  216. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
  217. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  218. package/Tests/UI/Utils/Cookie.test.ts +440 -0
  219. package/Tests/UI/Utils/ModelAPIRequestClient.test.ts +278 -0
  220. package/Tests/UI/Utils/Navigation.test.ts +33 -0
  221. package/Tests/UI/Utils/PricingPlan.test.ts +295 -0
  222. package/Tests/UI/Utils/Realtime.test.ts +759 -0
  223. package/Tests/UI/Utils/Telemetry/Telemetry.test.ts +500 -0
  224. package/Tests/Utils/APIInstanceDispatch.test.ts +311 -0
  225. package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizerSeverity.test.ts +565 -0
  226. package/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.ts +40 -0
  227. package/Types/Monitor/MonitorEvaluationSummary.ts +3 -0
  228. package/Types/OnCallDutyPolicy/ScheduleTimeline.ts +507 -0
  229. package/Types/OnCallDutyPolicy/ScheduleTimelineLayout.ts +589 -0
  230. package/Types/Realtime/EventName.ts +11 -0
  231. package/Types/SecurityEvent/DetectionFindingConstants.ts +14 -0
  232. package/Types/SecurityEvent/OcsfSeverity.ts +31 -26
  233. package/Types/SecurityEvent/SigmaRule.ts +51 -0
  234. package/Types/SecurityEvent/ThreatIntelConstants.ts +18 -0
  235. package/UI/Components/AttachmentList/EventAttachmentList.tsx +30 -2
  236. package/UI/Components/Dashboard/DashboardVariableControl.tsx +12 -2
  237. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  238. package/UI/Components/EditionLabel/EditionLabel.tsx +12 -0
  239. package/UI/Components/EventItem/EventItem.tsx +12 -1
  240. package/UI/Components/Forms/BasicForm.tsx +151 -126
  241. package/UI/Components/Forms/CollapsibleFormSection.tsx +63 -0
  242. package/UI/Components/Forms/Fields/FormField.tsx +10 -4
  243. package/UI/Components/Forms/FormSummary.tsx +7 -0
  244. package/UI/Components/Forms/Types/Field.ts +10 -0
  245. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +2 -1
  246. package/UI/Components/Markdown.tsx/SessionAwareImage.tsx +122 -0
  247. package/UI/Components/ModelTable/BaseModelTable.tsx +34 -5
  248. package/UI/Components/ModelTable/TableView.tsx +1 -1
  249. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +8 -1
  250. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  251. package/UI/Utils/API/API.ts +173 -12
  252. package/UI/Utils/API/RequestOptions.ts +11 -0
  253. package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +31 -7
  254. package/UI/Utils/ModelAPI/ModelAPI.ts +30 -9
  255. package/UI/Utils/Navigation.ts +9 -0
  256. package/UI/Utils/OpenAuthenticatedUrl.ts +154 -0
  257. package/UI/Utils/Realtime.ts +176 -30
  258. package/Utils/API.ts +14 -5
  259. package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +157 -7
  260. package/Utils/SecurityEvent/Sigma/SigmaRuleParser.ts +9 -17
  261. package/Utils/Slo/SloBurnRateTemplate.ts +2 -2
  262. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +1 -1
  263. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -1
  264. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +1 -1
  265. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -1
  266. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +1 -1
  267. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -1
  268. package/build/dist/Server/API/AIBillingAPI.js +1 -1
  269. package/build/dist/Server/API/AIBillingAPI.js.map +1 -1
  270. package/build/dist/Server/API/AIInsightAPI.js +1 -4
  271. package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
  272. package/build/dist/Server/API/AIInvestigationAPI.js +1 -4
  273. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  274. package/build/dist/Server/API/AIReadinessAPI.js +1 -3
  275. package/build/dist/Server/API/AIReadinessAPI.js.map +1 -1
  276. package/build/dist/Server/API/AlertAPI.js +1 -1
  277. package/build/dist/Server/API/AlertAPI.js.map +1 -1
  278. package/build/dist/Server/API/AutoRemediationAPI.js +1 -4
  279. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  280. package/build/dist/Server/API/BaseAPI.js +12 -0
  281. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  282. package/build/dist/Server/API/BaseAnalyticsAPI.js +55 -10
  283. package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
  284. package/build/dist/Server/API/BillingAPI.js +2 -2
  285. package/build/dist/Server/API/BillingAPI.js.map +1 -1
  286. package/build/dist/Server/API/BillingInvoiceAPI.js +1 -1
  287. package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
  288. package/build/dist/Server/API/BillingPaymentMethodAPI.js +2 -2
  289. package/build/dist/Server/API/BillingPaymentMethodAPI.js.map +1 -1
  290. package/build/dist/Server/API/CodeFixRunAPI.js +1 -3
  291. package/build/dist/Server/API/CodeFixRunAPI.js.map +1 -1
  292. package/build/dist/Server/API/CommonAPI.js +50 -5
  293. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  294. package/build/dist/Server/API/DashboardAPI.js +12 -12
  295. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  296. package/build/dist/Server/API/GlobalConfigAPI.js +12 -0
  297. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  298. package/build/dist/Server/API/IncidentAPI.js +2 -2
  299. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  300. package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
  301. package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
  302. package/build/dist/Server/API/NotificationAPI.js +1 -1
  303. package/build/dist/Server/API/NotificationAPI.js.map +1 -1
  304. package/build/dist/Server/API/OnCallCalendarAPI.js +1 -4
  305. package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -1
  306. package/build/dist/Server/API/OnCallDutyPolicyAPI.js +6 -0
  307. package/build/dist/Server/API/OnCallDutyPolicyAPI.js.map +1 -1
  308. package/build/dist/Server/API/OnCallReadinessAPI.js +4 -1
  309. package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -1
  310. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +494 -0
  311. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -0
  312. package/build/dist/Server/API/ProjectAPI.js +1 -1
  313. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  314. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
  315. package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
  316. package/build/dist/Server/API/StatusPageAPI.js +125 -33
  317. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  318. package/build/dist/Server/API/TeamMemberAPI.js +3 -3
  319. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  320. package/build/dist/Server/API/UserCallAPI.js +2 -2
  321. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  322. package/build/dist/Server/API/UserEmailAPI.js +2 -2
  323. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  324. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +2 -2
  325. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  326. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +1 -1
  327. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -1
  328. package/build/dist/Server/API/UserSlackAPI.js +1 -1
  329. package/build/dist/Server/API/UserSlackAPI.js.map +1 -1
  330. package/build/dist/Server/API/UserSmsAPI.js +2 -2
  331. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  332. package/build/dist/Server/API/UserTotpAuthAPI.js +1 -1
  333. package/build/dist/Server/API/UserTotpAuthAPI.js.map +1 -1
  334. package/build/dist/Server/API/UserWebAuthnAPI.js +2 -2
  335. package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -1
  336. package/build/dist/Server/API/UserWebhookAPI.js +1 -1
  337. package/build/dist/Server/API/UserWebhookAPI.js.map +1 -1
  338. package/build/dist/Server/API/UserWhatsAppAPI.js +2 -2
  339. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  340. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -2
  341. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  342. package/build/dist/Server/Middleware/UserAuthorization.js +64 -4
  343. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  344. package/build/dist/Server/Services/ApiKeyPermissionService.js +80 -4
  345. package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
  346. package/build/dist/Server/Services/DatabaseService.js +171 -0
  347. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  348. package/build/dist/Server/Services/DetectionRuleService.js +4 -4
  349. package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
  350. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js +170 -0
  351. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js.map +1 -0
  352. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -2
  353. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  354. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +2 -2
  355. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  356. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -2
  357. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  358. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -2
  359. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  360. package/build/dist/Server/Services/PayAsYouGoBillingService.js +20 -27
  361. package/build/dist/Server/Services/PayAsYouGoBillingService.js.map +1 -1
  362. package/build/dist/Server/Services/ProjectService.js +17 -0
  363. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  364. package/build/dist/Server/Services/RumSessionPinService.js +180 -16
  365. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  366. package/build/dist/Server/Services/TeamPermissionService.js +89 -27
  367. package/build/dist/Server/Services/TeamPermissionService.js.map +1 -1
  368. package/build/dist/Server/Services/ThreatIntelFeedService.js +5 -4
  369. package/build/dist/Server/Services/ThreatIntelFeedService.js.map +1 -1
  370. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +9 -6
  371. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  372. package/build/dist/Server/Types/Database/Permissions/Index.js +60 -6
  373. package/build/dist/Server/Types/Database/Permissions/Index.js.map +1 -1
  374. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +14 -13
  375. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  376. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +7 -0
  377. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -1
  378. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js +7 -0
  379. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js.map +1 -1
  380. package/build/dist/Server/Utils/MasterAdminPageAccess.js +39 -0
  381. package/build/dist/Server/Utils/MasterAdminPageAccess.js.map +1 -0
  382. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +3 -1
  383. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  384. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +3 -1
  385. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  386. package/build/dist/Server/Utils/Realtime.js +126 -37
  387. package/build/dist/Server/Utils/Realtime.js.map +1 -1
  388. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +7 -25
  389. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
  390. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js +127 -0
  391. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js.map +1 -0
  392. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +1 -1
  393. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
  394. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js +3 -2
  395. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js.map +1 -1
  396. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js +4 -8
  397. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js.map +1 -1
  398. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js +31 -1
  399. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js.map +1 -1
  400. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js +321 -0
  401. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js.map +1 -0
  402. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js +372 -0
  403. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js.map +1 -0
  404. package/build/dist/Types/Realtime/EventName.js +10 -0
  405. package/build/dist/Types/Realtime/EventName.js.map +1 -1
  406. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +11 -0
  407. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
  408. package/build/dist/Types/SecurityEvent/OcsfSeverity.js +30 -25
  409. package/build/dist/Types/SecurityEvent/OcsfSeverity.js.map +1 -1
  410. package/build/dist/Types/SecurityEvent/SigmaRule.js +47 -0
  411. package/build/dist/Types/SecurityEvent/SigmaRule.js.map +1 -1
  412. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js +14 -0
  413. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js.map +1 -1
  414. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js +9 -4
  415. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js.map +1 -1
  416. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +10 -2
  417. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -1
  418. package/build/dist/UI/Components/Dropdown/Dropdown.js +5 -1
  419. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  420. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +11 -0
  421. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  422. package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
  423. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  424. package/build/dist/UI/Components/Forms/BasicForm.js +70 -64
  425. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  426. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js +27 -0
  427. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js.map +1 -0
  428. package/build/dist/UI/Components/Forms/Fields/FormField.js +10 -7
  429. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  430. package/build/dist/UI/Components/Forms/FormSummary.js +7 -0
  431. package/build/dist/UI/Components/Forms/FormSummary.js.map +1 -1
  432. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +2 -1
  433. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  434. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js +84 -0
  435. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js.map +1 -0
  436. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +14 -1
  437. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  438. package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
  439. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  440. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -1
  441. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  442. package/build/dist/UI/Components/Tooltip/Tooltip.js +2 -1
  443. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  444. package/build/dist/UI/Utils/API/API.js +143 -19
  445. package/build/dist/UI/Utils/API/API.js.map +1 -1
  446. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +21 -7
  447. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
  448. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +26 -9
  449. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  450. package/build/dist/UI/Utils/Navigation.js +8 -0
  451. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  452. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js +92 -0
  453. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js.map +1 -0
  454. package/build/dist/UI/Utils/Realtime.js +120 -15
  455. package/build/dist/UI/Utils/Realtime.js.map +1 -1
  456. package/build/dist/Utils/API.js +13 -5
  457. package/build/dist/Utils/API.js.map +1 -1
  458. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +123 -6
  459. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
  460. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js +4 -18
  461. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js.map +1 -1
  462. package/build/dist/Utils/Slo/SloBurnRateTemplate.js +2 -2
  463. package/package.json +1 -1
@@ -0,0 +1,757 @@
1
+ import "@testing-library/jest-dom";
2
+ import {
3
+ afterEach,
4
+ beforeEach,
5
+ describe,
6
+ expect,
7
+ jest,
8
+ test,
9
+ } from "@jest/globals";
10
+ import {
11
+ cleanup,
12
+ fireEvent,
13
+ render,
14
+ screen,
15
+ waitFor,
16
+ } from "@testing-library/react";
17
+ import React, { ReactElement, useState } from "react";
18
+ import getJestMockFunction, { MockFunction } from "../../MockType";
19
+
20
+ /*
21
+ * Silent data loss in the affected-resources picker. A viewer whose role can
22
+ * edit an incident (IncidentMember) but cannot read some resource type
23
+ * (Kubernetes clusters here) opened the incident's Affected Resources card,
24
+ * clicked Edit, removed one monitor chip and saved - and every Kubernetes
25
+ * cluster attached to the incident was detached with it.
26
+ *
27
+ * The edit form loads those clusters as bare IDs (a `true` relation select
28
+ * comes back as `{ _id }` per resource, which ModelForm flattens to IDs).
29
+ * The picker hid the type from the viewer, which is right, but
30
+ * then rebuilt EVERY payload array from what it showed, so the hidden type
31
+ * went out as []. The page wrote that into the form and the many-to-many save
32
+ * cleared the junction rows. The picker now carries a type it is not showing
33
+ * through unchanged.
34
+ */
35
+
36
+ const getItemMock: MockFunction = getJestMockFunction();
37
+ const getListMock: MockFunction = getJestMockFunction();
38
+ const createOrUpdateMock: MockFunction = getJestMockFunction();
39
+
40
+ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
41
+ return {
42
+ __esModule: true,
43
+ default: {
44
+ getItem: (...args: Array<unknown>) => {
45
+ return getItemMock(...args);
46
+ },
47
+ getList: (...args: Array<unknown>) => {
48
+ return getListMock(...args);
49
+ },
50
+ createOrUpdate: (...args: Array<unknown>) => {
51
+ return createOrUpdateMock(...args);
52
+ },
53
+ },
54
+ };
55
+ });
56
+
57
+ let isMasterAdmin: boolean = false;
58
+ let projectPermissions: Array<Permission> = [];
59
+
60
+ jest.mock("../../../UI/Utils/User", () => {
61
+ return {
62
+ __esModule: true,
63
+ default: {
64
+ isMasterAdmin: (): boolean => {
65
+ return isMasterAdmin;
66
+ },
67
+ getUserId: (): null => {
68
+ return null;
69
+ },
70
+ },
71
+ };
72
+ });
73
+
74
+ jest.mock("../../../UI/Utils/Permission", () => {
75
+ return {
76
+ __esModule: true,
77
+ default: {
78
+ getAllPermissions: (): Array<Permission> => {
79
+ return projectPermissions;
80
+ },
81
+ getGlobalPermissions: (): null => {
82
+ return null;
83
+ },
84
+ getProjectPermissions: (): UserTenantAccessPermission => {
85
+ return {
86
+ _type: "UserTenantAccessPermission",
87
+ projectId: PROJECT_ID,
88
+ permissions: projectPermissions.map(
89
+ (permission: Permission): UserPermission => {
90
+ return {
91
+ _type: "UserPermission",
92
+ permission,
93
+ labelIds: [],
94
+ };
95
+ },
96
+ ),
97
+ };
98
+ },
99
+ },
100
+ };
101
+ });
102
+
103
+ import AffectedResourcesPicker, {
104
+ AffectedResourcesPayload,
105
+ AffectedResourceType,
106
+ isAffectedResourcesPayload,
107
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/AffectedResources/AffectedResourcesPicker";
108
+ import ModelForm, {
109
+ FormType,
110
+ ModelField,
111
+ } from "../../../UI/Components/Forms/ModelForm";
112
+ import { CustomElementProps } from "../../../UI/Components/Forms/Types/Field";
113
+ import FormFieldSchemaType from "../../../UI/Components/Forms/Types/FormFieldSchemaType";
114
+ import FormValues from "../../../UI/Components/Forms/Types/FormValues";
115
+ import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
116
+ import Host from "../../../Models/DatabaseModels/Host";
117
+ import Incident from "../../../Models/DatabaseModels/Incident";
118
+ import KubernetesCluster from "../../../Models/DatabaseModels/KubernetesCluster";
119
+ import Monitor from "../../../Models/DatabaseModels/Monitor";
120
+ import NetworkSite from "../../../Models/DatabaseModels/NetworkSite";
121
+ import Service from "../../../Models/DatabaseModels/Service";
122
+ import Includes from "../../../Types/BaseDatabase/Includes";
123
+ import ObjectID from "../../../Types/ObjectID";
124
+ import Permission, {
125
+ UserPermission,
126
+ UserTenantAccessPermission,
127
+ } from "../../../Types/Permission";
128
+
129
+ const PROJECT_ID: ObjectID = new ObjectID(
130
+ "99999999-9999-4999-8999-999999999999",
131
+ );
132
+
133
+ const id: (n: number) => string = (n: number): string => {
134
+ return `00000000-0000-4000-8000-${String(n).padStart(12, "0")}`;
135
+ };
136
+
137
+ const MONITOR_ID: string = id(1);
138
+ const MONITOR_NAME: string = "Checkout API";
139
+ const SECOND_MONITOR_ID: string = id(2);
140
+ const SECOND_MONITOR_NAME: string = "Orders DB";
141
+ const THIRD_MONITOR_ID: string = id(3);
142
+ const THIRD_MONITOR_NAME: string = "Payments";
143
+ const CLUSTER_ID: string = id(11);
144
+ const SECOND_CLUSTER_ID: string = id(12);
145
+ const HOST_ID: string = id(21);
146
+ const NETWORK_SITE_ID: string = id(31);
147
+
148
+ /*
149
+ * An IncidentMember: may edit an incident's affected resources and read
150
+ * monitors, but holds no permission that reads Kubernetes clusters (or
151
+ * hosts, services, ...).
152
+ */
153
+ const INCIDENT_MEMBER_WHO_READS_MONITORS: Array<Permission> = [
154
+ Permission.IncidentMember,
155
+ Permission.ReadProjectMonitor,
156
+ ];
157
+
158
+ // The types the incident page offers, in its order.
159
+ const INCIDENT_PAGE_TYPES: Array<AffectedResourceType> = [
160
+ "Monitor",
161
+ "Host",
162
+ "KubernetesCluster",
163
+ "DockerHost",
164
+ "PodmanHost",
165
+ "ProxmoxCluster",
166
+ "VMwareVCenter",
167
+ "CephCluster",
168
+ "DockerSwarmCluster",
169
+ "IoTFleet",
170
+ "Service",
171
+ ];
172
+
173
+ type ModelClass = { new (): BaseModel };
174
+
175
+ const NAMES: Map<ModelClass, Record<string, string>> = new Map<
176
+ ModelClass,
177
+ Record<string, string>
178
+ >([
179
+ [
180
+ Monitor,
181
+ {
182
+ [MONITOR_ID]: MONITOR_NAME,
183
+ [SECOND_MONITOR_ID]: SECOND_MONITOR_NAME,
184
+ [THIRD_MONITOR_ID]: THIRD_MONITOR_NAME,
185
+ },
186
+ ],
187
+ ]);
188
+
189
+ /*
190
+ * Answers the picker's search (every readable type) and, should the picker
191
+ * look names up by ID, those lookups too. Only monitors exist here.
192
+ */
193
+ const fakeGetList: (args: {
194
+ modelType: ModelClass;
195
+ query: Record<string, unknown>;
196
+ }) => Promise<{
197
+ data: Array<BaseModel>;
198
+ count: number;
199
+ skip: number;
200
+ limit: number;
201
+ }> = async (args: {
202
+ modelType: ModelClass;
203
+ query: Record<string, unknown>;
204
+ }): Promise<{
205
+ data: Array<BaseModel>;
206
+ count: number;
207
+ skip: number;
208
+ limit: number;
209
+ }> => {
210
+ const names: Record<string, string> = NAMES.get(args.modelType) || {};
211
+ const idFilter: unknown = args.query["_id"];
212
+ const ids: Array<string> =
213
+ idFilter instanceof Includes
214
+ ? (idFilter.values as Array<string>).map((v: string) => {
215
+ return String(v);
216
+ })
217
+ : Object.keys(names);
218
+ const data: Array<BaseModel> =
219
+ args.query["description"] !== undefined
220
+ ? []
221
+ : ids
222
+ .filter((rowId: string) => {
223
+ return names[rowId] !== undefined;
224
+ })
225
+ .map((rowId: string) => {
226
+ const model: BaseModel = new args.modelType();
227
+ model._id = rowId;
228
+ (model as unknown as { name: string }).name = names[rowId]!;
229
+ return model;
230
+ });
231
+ return { data, count: data.length, skip: 0, limit: 10 };
232
+ };
233
+
234
+ const named: (rowId: string, name: string) => { _id: string; name: string } = (
235
+ rowId: string,
236
+ name: string,
237
+ ): { _id: string; name: string } => {
238
+ return { _id: rowId, name };
239
+ };
240
+
241
+ type PickerValues = Partial<
242
+ Record<
243
+ Exclude<keyof AffectedResourcesPayload, "__affectedResourcesPayload">,
244
+ unknown
245
+ >
246
+ >;
247
+
248
+ let payloads: Array<AffectedResourcesPayload> = [];
249
+
250
+ const lastPayload: () => AffectedResourcesPayload =
251
+ (): AffectedResourcesPayload => {
252
+ expect(payloads.length).toBeGreaterThan(0);
253
+ return payloads[payloads.length - 1]!;
254
+ };
255
+
256
+ /*
257
+ * Stands in for a page: holds the form values, hands them to the picker, and
258
+ * writes every array of the payload back - which is what the incident, alert
259
+ * and scheduled maintenance pages all do.
260
+ */
261
+ const PageHarness: (props: {
262
+ initial: PickerValues;
263
+ resourceTypes?: Array<AffectedResourceType> | undefined;
264
+ }) => ReactElement = (props: {
265
+ initial: PickerValues;
266
+ resourceTypes?: Array<AffectedResourceType> | undefined;
267
+ }): ReactElement => {
268
+ const [values, setValues] = useState<PickerValues>(props.initial);
269
+ return (
270
+ <AffectedResourcesPicker
271
+ monitors={values.monitors as Array<Monitor>}
272
+ hosts={values.hosts as Array<Host>}
273
+ kubernetesClusters={values.kubernetesClusters as Array<KubernetesCluster>}
274
+ networkSites={values.networkSites as Array<NetworkSite>}
275
+ services={values.services as Array<Service>}
276
+ resourceTypes={props.resourceTypes}
277
+ onChange={(payload: AffectedResourcesPayload) => {
278
+ payloads.push(payload);
279
+ setValues({
280
+ ...values,
281
+ monitors: payload.monitors,
282
+ hosts: payload.hosts,
283
+ kubernetesClusters: payload.kubernetesClusters,
284
+ networkSites: payload.networkSites,
285
+ services: payload.services,
286
+ });
287
+ }}
288
+ />
289
+ );
290
+ };
291
+
292
+ beforeEach(() => {
293
+ isMasterAdmin = false;
294
+ projectPermissions = INCIDENT_MEMBER_WHO_READS_MONITORS;
295
+ payloads = [];
296
+ getItemMock.mockReset();
297
+ getListMock.mockReset();
298
+ createOrUpdateMock.mockReset();
299
+ getListMock.mockImplementation(async (...args: Array<unknown>) => {
300
+ return fakeGetList(
301
+ args[0] as { modelType: ModelClass; query: Record<string, unknown> },
302
+ );
303
+ });
304
+ });
305
+
306
+ afterEach(() => {
307
+ cleanup();
308
+ });
309
+
310
+ describe("AffectedResourcesPicker, for a viewer who cannot read every type", () => {
311
+ test("removing a monitor chip keeps the Kubernetes clusters the viewer cannot read", () => {
312
+ render(
313
+ <PageHarness
314
+ initial={{
315
+ monitors: [
316
+ named(MONITOR_ID, MONITOR_NAME),
317
+ named(SECOND_MONITOR_ID, SECOND_MONITOR_NAME),
318
+ ],
319
+ // Bare IDs - what ModelForm hands the picker in an edit form.
320
+ kubernetesClusters: [CLUSTER_ID, SECOND_CLUSTER_ID],
321
+ }}
322
+ resourceTypes={INCIDENT_PAGE_TYPES}
323
+ />,
324
+ );
325
+
326
+ fireEvent.click(
327
+ screen.getByRole("button", { name: `Remove ${MONITOR_NAME}` }),
328
+ );
329
+
330
+ expect(lastPayload().monitors).toEqual([SECOND_MONITOR_ID]);
331
+ expect(lastPayload().kubernetesClusters).toEqual([
332
+ CLUSTER_ID,
333
+ SECOND_CLUSTER_ID,
334
+ ]);
335
+ });
336
+
337
+ test("the clusters survive edit after edit, down to the last monitor", () => {
338
+ render(
339
+ <PageHarness
340
+ initial={{
341
+ monitors: [
342
+ named(MONITOR_ID, MONITOR_NAME),
343
+ named(SECOND_MONITOR_ID, SECOND_MONITOR_NAME),
344
+ ],
345
+ kubernetesClusters: [CLUSTER_ID, SECOND_CLUSTER_ID],
346
+ }}
347
+ resourceTypes={INCIDENT_PAGE_TYPES}
348
+ />,
349
+ );
350
+
351
+ fireEvent.click(
352
+ screen.getByRole("button", { name: `Remove ${MONITOR_NAME}` }),
353
+ );
354
+ fireEvent.click(
355
+ screen.getByRole("button", { name: `Remove ${SECOND_MONITOR_NAME}` }),
356
+ );
357
+
358
+ expect(payloads).toHaveLength(2);
359
+ // A type the viewer CAN read still goes out as [] once emptied.
360
+ expect(lastPayload().monitors).toEqual([]);
361
+ expect(lastPayload().kubernetesClusters).toEqual([
362
+ CLUSTER_ID,
363
+ SECOND_CLUSTER_ID,
364
+ ]);
365
+ });
366
+
367
+ test("adding a monitor from the search keeps the hidden clusters", async () => {
368
+ render(
369
+ <PageHarness
370
+ initial={{
371
+ monitors: [named(MONITOR_ID, MONITOR_NAME)],
372
+ kubernetesClusters: [CLUSTER_ID],
373
+ }}
374
+ resourceTypes={INCIDENT_PAGE_TYPES}
375
+ />,
376
+ );
377
+
378
+ fireEvent.focus(screen.getByRole("combobox"));
379
+ fireEvent.click(
380
+ await screen.findByRole("option", { name: THIRD_MONITOR_NAME }),
381
+ );
382
+
383
+ expect(lastPayload().monitors).toEqual([MONITOR_ID, THIRD_MONITOR_ID]);
384
+ expect(lastPayload().kubernetesClusters).toEqual([CLUSTER_ID]);
385
+ // Only the readable type was searched.
386
+ for (const call of getListMock.mock.calls) {
387
+ expect((call[0] as { modelType: ModelClass }).modelType).toBe(Monitor);
388
+ }
389
+ });
390
+
391
+ test("'Clear all' clears what the viewer can see and keeps what they cannot", () => {
392
+ const monitors: Array<{ _id: string; name: string }> = [];
393
+ for (let i: number = 0; i < 50; i++) {
394
+ monitors.push(named(id(1000 + i), `Monitor ${i}`));
395
+ }
396
+ render(
397
+ <PageHarness
398
+ initial={{ monitors, kubernetesClusters: [CLUSTER_ID] }}
399
+ resourceTypes={INCIDENT_PAGE_TYPES}
400
+ />,
401
+ );
402
+
403
+ fireEvent.click(screen.getByRole("button", { name: "Clear all" }));
404
+
405
+ expect(lastPayload().monitors).toEqual([]);
406
+ expect(lastPayload().kubernetesClusters).toEqual([CLUSTER_ID]);
407
+ });
408
+
409
+ test("Backspace on the empty input keeps the hidden clusters", () => {
410
+ render(
411
+ <PageHarness
412
+ initial={{
413
+ monitors: [named(MONITOR_ID, MONITOR_NAME)],
414
+ kubernetesClusters: [CLUSTER_ID],
415
+ }}
416
+ resourceTypes={INCIDENT_PAGE_TYPES}
417
+ />,
418
+ );
419
+
420
+ fireEvent.keyDown(screen.getByRole("combobox"), { key: "Backspace" });
421
+
422
+ expect(lastPayload().monitors).toEqual([]);
423
+ expect(lastPayload().kubernetesClusters).toEqual([CLUSTER_ID]);
424
+ });
425
+
426
+ test("hidden resources handed in as models go out as their IDs", () => {
427
+ const cluster: KubernetesCluster = new KubernetesCluster();
428
+ cluster._id = SECOND_CLUSTER_ID;
429
+ render(
430
+ <PageHarness
431
+ initial={{
432
+ monitors: [named(MONITOR_ID, MONITOR_NAME)],
433
+ kubernetesClusters: [{ _id: CLUSTER_ID }, cluster],
434
+ }}
435
+ resourceTypes={INCIDENT_PAGE_TYPES}
436
+ />,
437
+ );
438
+
439
+ fireEvent.click(
440
+ screen.getByRole("button", { name: `Remove ${MONITOR_NAME}` }),
441
+ );
442
+
443
+ expect(lastPayload().kubernetesClusters).toEqual([
444
+ CLUSTER_ID,
445
+ SECOND_CLUSTER_ID,
446
+ ]);
447
+ });
448
+
449
+ test("a hidden type the form never loaded stays undefined rather than []", () => {
450
+ render(
451
+ <PageHarness
452
+ initial={{
453
+ monitors: [named(MONITOR_ID, MONITOR_NAME)],
454
+ kubernetesClusters: [CLUSTER_ID],
455
+ hosts: [],
456
+ }}
457
+ resourceTypes={INCIDENT_PAGE_TYPES}
458
+ />,
459
+ );
460
+
461
+ fireEvent.click(
462
+ screen.getByRole("button", { name: `Remove ${MONITOR_NAME}` }),
463
+ );
464
+
465
+ /*
466
+ * undefined is left out of the save request, so the server keeps the
467
+ * relation as it is. [] would detach everything under it.
468
+ */
469
+ expect(lastPayload().services).toBeUndefined();
470
+ expect(lastPayload().dockerHosts).toBeUndefined();
471
+ // An empty array that WAS loaded is carried through as the same [].
472
+ expect(lastPayload().hosts).toEqual([]);
473
+ });
474
+
475
+ test("a type the page left out of resourceTypes is carried through too", () => {
476
+ isMasterAdmin = true;
477
+ render(
478
+ <PageHarness
479
+ initial={{
480
+ monitors: [named(MONITOR_ID, MONITOR_NAME)],
481
+ hosts: [HOST_ID],
482
+ networkSites: [{ _id: NETWORK_SITE_ID }],
483
+ }}
484
+ resourceTypes={["Monitor"]}
485
+ />,
486
+ );
487
+
488
+ fireEvent.click(
489
+ screen.getByRole("button", { name: `Remove ${MONITOR_NAME}` }),
490
+ );
491
+
492
+ expect(lastPayload().monitors).toEqual([]);
493
+ expect(lastPayload().hosts).toEqual([HOST_ID]);
494
+ expect(lastPayload().networkSites).toEqual([NETWORK_SITE_ID]);
495
+ });
496
+
497
+ test("says how many attached resources are hidden, without a removable chip for them", () => {
498
+ render(
499
+ <PageHarness
500
+ initial={{
501
+ monitors: [named(MONITOR_ID, MONITOR_NAME)],
502
+ hosts: [HOST_ID],
503
+ kubernetesClusters: [CLUSTER_ID, SECOND_CLUSTER_ID],
504
+ }}
505
+ resourceTypes={INCIDENT_PAGE_TYPES}
506
+ />,
507
+ );
508
+
509
+ expect(
510
+ screen.getByTestId("affected-resources-hidden-note"),
511
+ ).toHaveTextContent(
512
+ "3 resources you don't have permission to view are also attached (Host, Kubernetes Cluster). They will be kept.",
513
+ );
514
+ // One chip - the monitor - and one remove button.
515
+ expect(screen.getAllByRole("button", { name: /^Remove / })).toHaveLength(1);
516
+ expect(screen.queryByText("Kubernetes Cluster")).toBeNull();
517
+ expect(screen.queryByText(/^Unnamed /)).toBeNull();
518
+ });
519
+
520
+ test("uses the singular for a single hidden resource", () => {
521
+ render(
522
+ <PageHarness
523
+ initial={{ kubernetesClusters: [CLUSTER_ID] }}
524
+ resourceTypes={INCIDENT_PAGE_TYPES}
525
+ />,
526
+ );
527
+
528
+ expect(
529
+ screen.getByTestId("affected-resources-hidden-note"),
530
+ ).toHaveTextContent(
531
+ "1 resource you don't have permission to view is also attached (Kubernetes Cluster). It will be kept.",
532
+ );
533
+ });
534
+
535
+ test("shows no note when the viewer can read every attached type", () => {
536
+ isMasterAdmin = true;
537
+ render(
538
+ <PageHarness
539
+ initial={{
540
+ monitors: [named(MONITOR_ID, MONITOR_NAME)],
541
+ kubernetesClusters: [CLUSTER_ID],
542
+ }}
543
+ resourceTypes={INCIDENT_PAGE_TYPES}
544
+ />,
545
+ );
546
+
547
+ expect(screen.queryByTestId("affected-resources-hidden-note")).toBeNull();
548
+ expect(screen.getAllByRole("button", { name: /^Remove / })).toHaveLength(2);
549
+ });
550
+
551
+ test("shows no note when a denied type has nothing attached", () => {
552
+ render(
553
+ <PageHarness
554
+ initial={{
555
+ monitors: [named(MONITOR_ID, MONITOR_NAME)],
556
+ kubernetesClusters: [],
557
+ }}
558
+ resourceTypes={INCIDENT_PAGE_TYPES}
559
+ />,
560
+ );
561
+
562
+ expect(screen.queryByTestId("affected-resources-hidden-note")).toBeNull();
563
+ });
564
+ });
565
+
566
+ /*
567
+ * The whole path as the viewer met it, through the real ModelForm: the
568
+ * incident loads with its relations as `{ _id }`, ModelForm flattens them to
569
+ * IDs, the picker edits, the page's onChange writes every array back, and
570
+ * ModelForm submits.
571
+ */
572
+
573
+ const INCIDENT_ID: ObjectID = new ObjectID(
574
+ "11111111-1111-4111-8111-111111111111",
575
+ );
576
+
577
+ const hiddenField: (
578
+ key: "hosts" | "kubernetesClusters" | "services",
579
+ ) => ModelField<Incident> = (
580
+ key: "hosts" | "kubernetesClusters" | "services",
581
+ ): ModelField<Incident> => {
582
+ return {
583
+ field: { [key]: true } as ModelField<Incident>["field"],
584
+ title: "",
585
+ fieldType: FormFieldSchemaType.Text,
586
+ required: false,
587
+ showIf: () => {
588
+ return false;
589
+ },
590
+ };
591
+ };
592
+
593
+ // The incident page's Affected Resources edit fields.
594
+ const AFFECTED_RESOURCE_FIELDS: Array<ModelField<Incident>> = [
595
+ {
596
+ field: { monitors: true },
597
+ title: "",
598
+ fieldType: FormFieldSchemaType.CustomComponent,
599
+ required: false,
600
+ getCustomElement: (
601
+ values: FormValues<Incident>,
602
+ elementProps: CustomElementProps,
603
+ ): ReactElement => {
604
+ return (
605
+ <AffectedResourcesPicker
606
+ monitors={values.monitors as Array<Monitor>}
607
+ hosts={values.hosts as Array<Host>}
608
+ kubernetesClusters={
609
+ values.kubernetesClusters as Array<KubernetesCluster>
610
+ }
611
+ services={values.services as Array<Service>}
612
+ resourceTypes={INCIDENT_PAGE_TYPES}
613
+ onChange={(payload: unknown) => {
614
+ elementProps.onChange?.(payload);
615
+ }}
616
+ />
617
+ );
618
+ },
619
+ onChange: (
620
+ value: unknown,
621
+ currentValues: FormValues<Incident>,
622
+ setNewFormValues: (values: FormValues<Incident>) => void,
623
+ ) => {
624
+ if (isAffectedResourcesPayload(value)) {
625
+ const payload: typeof value = value;
626
+ payloads.push(payload);
627
+ queueMicrotask(() => {
628
+ setNewFormValues({
629
+ ...currentValues,
630
+ monitors: payload.monitors,
631
+ hosts: payload.hosts,
632
+ kubernetesClusters: payload.kubernetesClusters,
633
+ dockerHosts: payload.dockerHosts,
634
+ podmanHosts: payload.podmanHosts,
635
+ proxmoxClusters: payload.proxmoxClusters,
636
+ vmwareVCenters: payload.vmwareVCenters,
637
+ cephClusters: payload.cephClusters,
638
+ dockerSwarmClusters: payload.dockerSwarmClusters,
639
+ iotFleets: payload.iotFleets,
640
+ services: payload.services,
641
+ } as FormValues<Incident>);
642
+ });
643
+ }
644
+ },
645
+ },
646
+ hiddenField("hosts"),
647
+ hiddenField("kubernetesClusters"),
648
+ hiddenField("services"),
649
+ ];
650
+
651
+ const renderEditForm: () => void = (): void => {
652
+ render(
653
+ <ModelForm<Incident>
654
+ modelType={Incident}
655
+ id="edit-incident-affected-resources"
656
+ name="Edit Incident"
657
+ fields={AFFECTED_RESOURCE_FIELDS}
658
+ formType={FormType.Update}
659
+ modelIdToEdit={INCIDENT_ID}
660
+ submitButtonText="Save Changes"
661
+ onSuccess={() => {
662
+ // asserted through createOrUpdateMock
663
+ }}
664
+ />,
665
+ );
666
+ };
667
+
668
+ const idsOf: (models: Array<BaseModel> | undefined) => Array<string> = (
669
+ models: Array<BaseModel> | undefined,
670
+ ): Array<string> => {
671
+ return (models || []).map((model: BaseModel) => {
672
+ return String(model._id);
673
+ });
674
+ };
675
+
676
+ const saveAndGetSavedIncident: () => Promise<Incident> =
677
+ async (): Promise<Incident> => {
678
+ fireEvent.click(screen.getByRole("button", { name: "Save Changes" }));
679
+ await waitFor(() => {
680
+ expect(createOrUpdateMock).toHaveBeenCalledTimes(1);
681
+ });
682
+ return (createOrUpdateMock.mock.calls[0]![0] as { model: Incident }).model;
683
+ };
684
+
685
+ describe("the incident Affected Resources Edit modal, for an IncidentMember who cannot read Kubernetes clusters", () => {
686
+ beforeEach(() => {
687
+ getItemMock.mockImplementation(async () => {
688
+ // A `true` relation select comes back as `{ _id }` per resource.
689
+ return BaseModel.fromJSON(
690
+ {
691
+ _id: INCIDENT_ID.toString(),
692
+ monitors: [{ _id: MONITOR_ID }, { _id: SECOND_MONITOR_ID }],
693
+ hosts: [{ _id: HOST_ID }],
694
+ kubernetesClusters: [{ _id: CLUSTER_ID }, { _id: SECOND_CLUSTER_ID }],
695
+ services: [],
696
+ },
697
+ Incident,
698
+ ) as Incident;
699
+ });
700
+ createOrUpdateMock.mockImplementation(async () => {
701
+ return { data: new Incident(), miscData: undefined };
702
+ });
703
+ });
704
+
705
+ test("removing a monitor keeps the clusters in the payload and in the saved incident", async () => {
706
+ renderEditForm();
707
+
708
+ const removeButtons: Array<HTMLElement> = await screen.findAllByRole(
709
+ "button",
710
+ { name: /^Remove / },
711
+ );
712
+ // Only the two monitors get a chip; the clusters and the host are hidden.
713
+ expect(removeButtons).toHaveLength(2);
714
+ expect(
715
+ screen.getByTestId("affected-resources-hidden-note"),
716
+ ).toHaveTextContent(
717
+ "3 resources you don't have permission to view are also attached (Host, Kubernetes Cluster). They will be kept.",
718
+ );
719
+
720
+ fireEvent.click(removeButtons[0]!);
721
+ await waitFor(() => {
722
+ expect(screen.getAllByRole("button", { name: /^Remove / })).toHaveLength(
723
+ 1,
724
+ );
725
+ });
726
+
727
+ expect(lastPayload().monitors).toEqual([SECOND_MONITOR_ID]);
728
+ expect(lastPayload().kubernetesClusters).toEqual([
729
+ CLUSTER_ID,
730
+ SECOND_CLUSTER_ID,
731
+ ]);
732
+ expect(lastPayload().hosts).toEqual([HOST_ID]);
733
+
734
+ const saved: Incident = await saveAndGetSavedIncident();
735
+ expect(idsOf(saved.monitors)).toEqual([SECOND_MONITOR_ID]);
736
+ expect(idsOf(saved.kubernetesClusters)).toEqual([
737
+ CLUSTER_ID,
738
+ SECOND_CLUSTER_ID,
739
+ ]);
740
+ expect(idsOf(saved.hosts)).toEqual([HOST_ID]);
741
+ });
742
+
743
+ test("saving without touching the picker keeps every attached resource", async () => {
744
+ renderEditForm();
745
+
746
+ await screen.findAllByRole("button", { name: /^Remove / });
747
+ const saved: Incident = await saveAndGetSavedIncident();
748
+
749
+ expect(payloads).toHaveLength(0);
750
+ expect(idsOf(saved.monitors)).toEqual([MONITOR_ID, SECOND_MONITOR_ID]);
751
+ expect(idsOf(saved.kubernetesClusters)).toEqual([
752
+ CLUSTER_ID,
753
+ SECOND_CLUSTER_ID,
754
+ ]);
755
+ expect(idsOf(saved.hosts)).toEqual([HOST_ID]);
756
+ });
757
+ });