@oneuptime/common 14.0.10 → 14.0.11

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 (837) hide show
  1. package/Models/DatabaseModels/AIConversation.ts +127 -1
  2. package/Models/DatabaseModels/CephCluster.ts +12 -0
  3. package/Models/DatabaseModels/CloudResource.ts +12 -0
  4. package/Models/DatabaseModels/DatabaseServer.ts +12 -0
  5. package/Models/DatabaseModels/DockerHost.ts +12 -0
  6. package/Models/DatabaseModels/DockerSwarmCluster.ts +12 -0
  7. package/Models/DatabaseModels/GlobalOidc.ts +0 -4
  8. package/Models/DatabaseModels/GlobalOidcProject.ts +0 -4
  9. package/Models/DatabaseModels/GlobalSso.ts +0 -4
  10. package/Models/DatabaseModels/GlobalSsoProject.ts +0 -4
  11. package/Models/DatabaseModels/Host.ts +12 -0
  12. package/Models/DatabaseModels/IncidentForm.ts +594 -0
  13. package/Models/DatabaseModels/IncidentFormSubmission.ts +283 -0
  14. package/Models/DatabaseModels/IncidentTemplate.ts +59 -0
  15. package/Models/DatabaseModels/Index.ts +14 -0
  16. package/Models/DatabaseModels/IoTFleet.ts +12 -0
  17. package/Models/DatabaseModels/KubernetesCluster.ts +12 -0
  18. package/Models/DatabaseModels/MessageQueue.ts +1102 -0
  19. package/Models/DatabaseModels/MessageQueueLabelRule.ts +553 -0
  20. package/Models/DatabaseModels/MessageQueueOwnerRule.ts +633 -0
  21. package/Models/DatabaseModels/MessageQueueOwnerTeam.ts +491 -0
  22. package/Models/DatabaseModels/MessageQueueOwnerUser.ts +490 -0
  23. package/Models/DatabaseModels/PodmanHost.ts +12 -0
  24. package/Models/DatabaseModels/Project.ts +59 -0
  25. package/Models/DatabaseModels/ProjectOidc.ts +0 -4
  26. package/Models/DatabaseModels/ProjectSso.ts +0 -4
  27. package/Models/DatabaseModels/ProxmoxCluster.ts +12 -0
  28. package/Models/DatabaseModels/RumApplication.ts +12 -0
  29. package/Models/DatabaseModels/ServerlessFunction.ts +12 -0
  30. package/Models/DatabaseModels/Service.ts +12 -0
  31. package/Models/DatabaseModels/StatusPageOidc.ts +0 -4
  32. package/Models/DatabaseModels/StatusPageSso.ts +0 -4
  33. package/Models/DatabaseModels/TeamComplianceSetting.ts +47 -2
  34. package/Models/DatabaseModels/VMwareVCenter.ts +12 -0
  35. package/Server/API/AIChatAPI.ts +13 -114
  36. package/Server/API/AIInvestigationAPI.ts +3 -3
  37. package/Server/API/AIInvestigationConversationAPI.ts +266 -0
  38. package/Server/API/IncidentFormAPI.ts +376 -0
  39. package/Server/API/ProjectOIDC.ts +16 -25
  40. package/Server/API/ProjectSSO.ts +16 -24
  41. package/Server/API/StatusPageAPI.ts +42 -77
  42. package/Server/Enterprise/EnterpriseEdition.ts +93 -28
  43. package/Server/Enterprise/EnterpriseFeature.ts +25 -3
  44. package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +1 -1
  45. package/Server/Enterprise/EnterpriseServerModule.ts +4 -1
  46. package/Server/EnvironmentConfig.ts +1 -1
  47. package/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.ts +129 -0
  48. package/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.ts +52 -0
  49. package/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.ts +75 -0
  50. package/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.ts +477 -0
  51. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  52. package/Server/Middleware/IncidentFormRateLimit.ts +847 -0
  53. package/Server/Middleware/UserAuthorization.ts +16 -38
  54. package/Server/Services/AIService.ts +10 -13
  55. package/Server/Services/IncidentCustomFieldService.ts +112 -2
  56. package/Server/Services/IncidentEpisodeMemberService.ts +24 -4
  57. package/Server/Services/IncidentEpisodeService.ts +10 -1
  58. package/Server/Services/IncidentFormService.ts +1566 -0
  59. package/Server/Services/IncidentFormSubmissionService.ts +97 -0
  60. package/Server/Services/IncidentGroupingEngineService.ts +45 -8
  61. package/Server/Services/IncidentService.ts +123 -5
  62. package/Server/Services/IncidentTemplateService.ts +22 -0
  63. package/Server/Services/Index.ts +14 -0
  64. package/Server/Services/MessageQueueLabelRuleEngineService.ts +321 -0
  65. package/Server/Services/MessageQueueLabelRuleService.ts +14 -0
  66. package/Server/Services/MessageQueueOwnerRuleEngineService.ts +412 -0
  67. package/Server/Services/MessageQueueOwnerRuleService.ts +14 -0
  68. package/Server/Services/MessageQueueOwnerTeamService.ts +62 -0
  69. package/Server/Services/MessageQueueOwnerUserService.ts +62 -0
  70. package/Server/Services/MessageQueueService.ts +1463 -0
  71. package/Server/Services/OpenTelemetryIngestService.ts +37 -4
  72. package/Server/Services/ProjectService.ts +0 -59
  73. package/Server/Services/StatusPagePrivateUserService.ts +1 -4
  74. package/Server/Services/StatusPageService.ts +1 -51
  75. package/Server/Services/TeamComplianceSettingService.ts +163 -42
  76. package/Server/Services/UserNotificationRuleService.ts +8 -1
  77. package/Server/Services/WorkspaceNotificationSummaryService.ts +12 -3
  78. package/Server/Types/Database/Permissions/CreatePermission.ts +17 -3
  79. package/Server/Types/Database/Permissions/EditionPermission.ts +140 -38
  80. package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +16 -0
  81. package/Server/Types/Database/Permissions/TablePermission.ts +3 -3
  82. package/Server/Types/Database/Permissions/UpdatePermission.ts +20 -6
  83. package/Server/Utils/AI/Chat/AgentContextCompactor.ts +200 -0
  84. package/Server/Utils/AI/Chat/ChatAgentRunner.ts +307 -48
  85. package/Server/Utils/AI/Chat/ChatRunCancellation.ts +156 -0
  86. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +78 -18
  87. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
  88. package/Server/Utils/AI/Chat/ToolOutputPager.ts +322 -0
  89. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +88 -30
  90. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +13 -1
  91. package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +0 -32
  92. package/Server/Utils/AI/CodeFix/FixRunBudget.ts +27 -25
  93. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +7 -1
  94. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +34 -14
  95. package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +69 -24
  96. package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +9 -0
  97. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +53 -10
  98. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +27 -10
  99. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +27 -10
  100. package/Server/Utils/AI/SRE/InvestigationQueue.ts +16 -19
  101. package/Server/Utils/AI/SRE/InvestigationThread.ts +646 -0
  102. package/Server/Utils/AI/SRE/InvestigationThreadService.ts +715 -0
  103. package/Server/Utils/AI/Toolbox/Serializer.ts +10 -3
  104. package/Server/Utils/EditionEnforcement.ts +19 -202
  105. package/Server/Utils/LLM/LLMService.ts +259 -63
  106. package/Server/Utils/RelaxedScimTeamLocksReport.ts +245 -0
  107. package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
  108. package/Server/Utils/SameOriginRequest.ts +203 -0
  109. package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
  110. package/Server/Utils/Telemetry/EntityRegistry.ts +4 -1
  111. package/Server/Utils/Telemetry/MessageQueueDiscovery.ts +1402 -0
  112. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +4 -1
  113. package/Server/Utils/VendorAssets.ts +3 -3
  114. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +3 -1
  115. package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +3 -1
  116. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +10 -1
  117. package/Tests/App/Accounts/IncidentFormPage.test.tsx +2036 -0
  118. package/Tests/App/Accounts/IncidentFormServerMessages.test.ts +119 -0
  119. package/Tests/App/Accounts/IncidentFormUtils.test.ts +1157 -0
  120. package/Tests/App/Accounts/LoginPasskey.test.tsx +6 -4
  121. package/Tests/App/Accounts/LoginSso.test.tsx +724 -0
  122. package/Tests/App/AdminDashboard/AdminAuthenticationRequireSso.test.tsx +338 -0
  123. package/Tests/App/AdminDashboard/AdminAuthenticationSsoCardLocales.test.tsx +331 -0
  124. package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +47 -33
  125. package/Tests/App/AdminDashboard/GlobalSsoOidcPages.test.tsx +1021 -0
  126. package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +876 -78
  127. package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +20 -3
  128. package/Tests/App/Dashboard/CephProxmoxVMwareDocumentationCards.test.tsx +359 -0
  129. package/Tests/App/Dashboard/CephSetupGuide.test.ts +961 -0
  130. package/Tests/App/Dashboard/CloudDocumentationCard.test.tsx +381 -95
  131. package/Tests/App/Dashboard/CloudSetupGuide.test.ts +1463 -0
  132. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +174 -12
  133. package/Tests/App/Dashboard/ContainerSetupGuideCards.test.tsx +345 -0
  134. package/Tests/App/Dashboard/CoreListEmptyStates.test.tsx +501 -0
  135. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +0 -1
  136. package/Tests/App/Dashboard/DatabaseDocumentationCard.test.tsx +400 -0
  137. package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +5 -0
  138. package/Tests/App/Dashboard/DatabaseSetupGuide.test.ts +1869 -0
  139. package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +44 -0
  140. package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +50 -9
  141. package/Tests/App/Dashboard/DockerSetupGuide.test.ts +899 -0
  142. package/Tests/App/Dashboard/DockerSwarmSetupGuide.test.ts +964 -0
  143. package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +141 -7
  144. package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +52 -39
  145. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +7 -1
  146. package/Tests/App/Dashboard/GettingStartedTaskRoutes.test.tsx +331 -0
  147. package/Tests/App/Dashboard/HomeOverviewStatsFirstRun.test.tsx +925 -0
  148. package/Tests/App/Dashboard/HostDocumentationCard.test.tsx +225 -0
  149. package/Tests/App/Dashboard/HostSetupGuide.test.ts +1183 -0
  150. package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +69 -45
  151. package/Tests/App/Dashboard/IncidentCreateTemplateCustomFieldSettings.test.tsx +745 -0
  152. package/Tests/App/Dashboard/IncidentCreateTemplateOwners.test.tsx +414 -0
  153. package/Tests/App/Dashboard/IncidentCustomFieldCreateSettingsForm.test.ts +1042 -0
  154. package/Tests/App/Dashboard/IncidentCustomFieldSettingsCard.test.tsx +1816 -0
  155. package/Tests/App/Dashboard/IncidentFormPlan.test.ts +89 -0
  156. package/Tests/App/Dashboard/IncidentFormShareLinkCard.test.tsx +846 -0
  157. package/Tests/App/Dashboard/IncidentFormsPages.test.tsx +1324 -0
  158. package/Tests/App/Dashboard/IncidentTemplateCustomFieldSettings.test.tsx +630 -0
  159. package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +13 -0
  160. package/Tests/App/Dashboard/IncomingEmailMonitorLinkVerifyHint.test.tsx +104 -0
  161. package/Tests/App/Dashboard/InvestigationConversation.test.tsx +1107 -0
  162. package/Tests/App/Dashboard/InvestigationConversationData.test.ts +546 -0
  163. package/Tests/App/Dashboard/InvestigationEvidenceList.test.tsx +170 -5
  164. package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +207 -2
  165. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +729 -34
  166. package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +9 -21
  167. package/Tests/App/Dashboard/InvestigationReportView.test.tsx +274 -37
  168. package/Tests/App/Dashboard/InvestigationStatusBadge.test.tsx +130 -0
  169. package/Tests/App/Dashboard/IoTDocumentationCard.test.tsx +218 -0
  170. package/Tests/App/Dashboard/IoTSetupGuide.test.ts +1124 -0
  171. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +20 -6
  172. package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +107 -83
  173. package/Tests/App/Dashboard/KubernetesSetupGuide.test.ts +787 -0
  174. package/Tests/App/Dashboard/MessageQueueBreadcrumbs.test.tsx +228 -0
  175. package/Tests/App/Dashboard/MessageQueueBrokerHealth.test.tsx +1538 -0
  176. package/Tests/App/Dashboard/MessageQueueBrokerHealthMetricChart.test.tsx +969 -0
  177. package/Tests/App/Dashboard/MessageQueueBrokerHealthReads.test.ts +889 -0
  178. package/Tests/App/Dashboard/MessageQueueBrokerMetricsReach.test.ts +236 -0
  179. package/Tests/App/Dashboard/MessageQueueDocumentationCard.test.tsx +479 -0
  180. package/Tests/App/Dashboard/MessageQueueListPages.test.tsx +1232 -0
  181. package/Tests/App/Dashboard/MessageQueueMetricDescriptions.test.ts +244 -0
  182. package/Tests/App/Dashboard/MessageQueueMonitorLink.test.ts +952 -0
  183. package/Tests/App/Dashboard/MessageQueueMonitorLinkMonitorCreate.test.tsx +859 -0
  184. package/Tests/App/Dashboard/MessageQueueOverview.test.tsx +1773 -0
  185. package/Tests/App/Dashboard/MessageQueueOverviewPresentation.test.ts +196 -0
  186. package/Tests/App/Dashboard/MessageQueuePresentation.test.ts +799 -0
  187. package/Tests/App/Dashboard/MessageQueueProducersConsumers.test.tsx +680 -0
  188. package/Tests/App/Dashboard/MessageQueueRoutes.test.tsx +383 -0
  189. package/Tests/App/Dashboard/MessageQueueRulePages.test.tsx +278 -0
  190. package/Tests/App/Dashboard/MessageQueueSetupGuide.test.ts +2778 -0
  191. package/Tests/App/Dashboard/MessageQueueSideMenus.test.tsx +273 -0
  192. package/Tests/App/Dashboard/MessageQueueSpanPopulations.test.ts +930 -0
  193. package/Tests/App/Dashboard/MessageQueueSpanStore.ts +351 -0
  194. package/Tests/App/Dashboard/MessageQueueTelemetryScope.test.ts +375 -0
  195. package/Tests/App/Dashboard/MessageQueueTelemetryScopeTabs.test.tsx +668 -0
  196. package/Tests/App/Dashboard/MessageQueueViewPages.test.tsx +685 -0
  197. package/Tests/App/Dashboard/MonitorAndHomeNotOperationalMenus.test.tsx +342 -0
  198. package/Tests/App/Dashboard/MonitorCreateFirstRunDefaults.test.tsx +623 -0
  199. package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +442 -0
  200. package/Tests/App/Dashboard/MonitorLogsPageViewSummary.test.tsx +814 -0
  201. package/Tests/App/Dashboard/MonitorSetupCardBodies.test.tsx +246 -0
  202. package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +8 -0
  203. package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +27 -12
  204. package/Tests/App/Dashboard/PlanGatedPage.test.tsx +292 -0
  205. package/Tests/App/Dashboard/PodmanSetupGuide.test.ts +948 -0
  206. package/Tests/App/Dashboard/ProductOrientation.test.tsx +5 -4
  207. package/Tests/App/Dashboard/ProductSettingsSectionCollapsed.test.tsx +266 -0
  208. package/Tests/App/Dashboard/ProxmoxSetupGuide.test.ts +1318 -0
  209. package/Tests/App/Dashboard/ResourceAiAgentAttentionServerParity.test.ts +555 -0
  210. package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +298 -9
  211. package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +453 -0
  212. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +16 -0
  213. package/Tests/App/Dashboard/ResourceConnectionGuideCard.test.tsx +444 -0
  214. package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsInfra.test.tsx +798 -0
  215. package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsPlatforms.test.tsx +717 -0
  216. package/Tests/App/Dashboard/ResourceConnectionGuides.test.ts +981 -0
  217. package/Tests/App/Dashboard/ResourceConnectionState.test.ts +132 -0
  218. package/Tests/App/Dashboard/RumDocumentationCard.test.tsx +293 -0
  219. package/Tests/App/Dashboard/RumSessionReplayRetentionSettings.test.tsx +467 -0
  220. package/Tests/App/Dashboard/RumSetupGuide.test.ts +758 -0
  221. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +25 -0
  222. package/Tests/App/Dashboard/ServerlessDocumentationCard.test.tsx +254 -0
  223. package/Tests/App/Dashboard/ServerlessSetupGuide.test.ts +1074 -0
  224. package/Tests/App/Dashboard/SetupGuide.test.ts +246 -0
  225. package/Tests/App/Dashboard/SetupGuideCard.test.tsx +756 -0
  226. package/Tests/App/Dashboard/SloListSideMenu.test.tsx +120 -0
  227. package/Tests/App/Dashboard/SsoPages.test.tsx +833 -0
  228. package/Tests/App/Dashboard/TelemetryDocumentation.test.tsx +253 -0
  229. package/Tests/App/Dashboard/TelemetryRetentionShells.test.tsx +730 -0
  230. package/Tests/App/Dashboard/VMwareSetupGuide.test.ts +1033 -0
  231. package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +87 -7
  232. package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +28 -9
  233. package/Tests/Models/DatabaseModels/IncidentFormModel.test.ts +1250 -0
  234. package/Tests/Models/DatabaseModels/MessageQueueModels.test.ts +1983 -0
  235. package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
  236. package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +190 -14
  237. package/Tests/Models/IncidentTemplateCustomFieldSettingsColumn.test.ts +291 -0
  238. package/Tests/Server/API/AIInvestigationConversationAPI.test.ts +412 -0
  239. package/Tests/Server/API/GlobalConfigLicense.test.ts +38 -9
  240. package/Tests/Server/API/IncidentFormAPI.test.ts +942 -0
  241. package/Tests/Server/API/IncidentFormPublicRoutes.test.ts +1653 -0
  242. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -1
  243. package/Tests/Server/API/{SsoProviderListingsEdition.test.ts → SsoProviderListings.test.ts} +164 -110
  244. package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +76 -54
  245. package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +125 -42
  246. package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +36 -10
  247. package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +51 -9
  248. package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +13 -8
  249. package/Tests/Server/Enterprise/SsoInCommunityEdition.test.ts +540 -0
  250. package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
  251. package/Tests/Server/Infrastructure/Postgres/AddIncidentFormsMigration.test.ts +438 -0
  252. package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleNotificationChannelsMigration.test.ts +255 -0
  253. package/Tests/Server/Infrastructure/Postgres/MessageQueueTablesMigration.test.ts +637 -0
  254. package/Tests/Server/Middleware/IncidentFormRateLimit.test.ts +1630 -0
  255. package/Tests/Server/Middleware/UserAuthorization.test.ts +16 -17
  256. package/Tests/Server/Middleware/UserAuthorizationSsoEnforcement.test.ts +544 -0
  257. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +37 -8
  258. package/Tests/Server/Services/IncidentCreatedFeedTitle.test.ts +212 -0
  259. package/Tests/Server/Services/IncidentCustomFieldDeleteFormQuestions.test.ts +297 -0
  260. package/Tests/Server/Services/IncidentFormOwnersCreatedNotification.test.ts +497 -0
  261. package/Tests/Server/Services/IncidentFormPostgres.test.ts +958 -0
  262. package/Tests/Server/Services/IncidentFormPublicForm.test.ts +1260 -0
  263. package/Tests/Server/Services/IncidentFormPublicSubmission.test.ts +2520 -0
  264. package/Tests/Server/Services/IncidentFormRegistration.test.ts +159 -0
  265. package/Tests/Server/Services/IncidentFormService.test.ts +663 -0
  266. package/Tests/Server/Services/IncidentFormSubmissionService.test.ts +430 -0
  267. package/Tests/Server/Services/IncidentFormSubmissionTemplateBranch.test.ts +473 -0
  268. package/Tests/Server/Services/IncidentGroupingTemplateSubstitution.test.ts +81 -0
  269. package/Tests/Server/Services/IncidentTemplateCustomFieldSettings.test.ts +262 -0
  270. package/Tests/Server/Services/IncidentTemplateOwnersOnDeclare.test.ts +566 -0
  271. package/Tests/Server/Services/IncidentTitleMarkdownSinks.test.ts +640 -0
  272. package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +3 -3
  273. package/Tests/Server/Services/MessageQueueOwnerServices.test.ts +440 -0
  274. package/Tests/Server/Services/MessageQueueRegistries.test.ts +577 -0
  275. package/Tests/Server/Services/MessageQueueRuleEngines.test.ts +900 -0
  276. package/Tests/Server/Services/MessageQueueService.test.ts +2210 -0
  277. package/Tests/Server/Services/MessageQueueSqlPostgres.test.ts +1271 -0
  278. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +28 -1
  279. package/Tests/Server/Services/OpenTelemetryRetentionOverrideEdition.test.ts +474 -0
  280. package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +21 -0
  281. package/Tests/Server/Services/OwnerRowUniqueness.test.ts +3 -0
  282. package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +0 -1
  283. package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +56 -6
  284. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -1
  285. package/Tests/Server/Services/SsoRequirementReads.test.ts +454 -0
  286. package/Tests/Server/Services/SsoRequirementWrites.test.ts +529 -0
  287. package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
  288. package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +107 -89
  289. package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +1068 -176
  290. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +1962 -200
  291. package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +654 -297
  292. package/Tests/Server/Types/Database/Permissions/RetentionOverrideColumns.test.ts +784 -0
  293. package/Tests/Server/Types/Database/Permissions/SsoScalePlanGate.test.ts +418 -0
  294. package/Tests/Server/Utils/AI/AIAlertGating.test.ts +0 -1
  295. package/Tests/Server/Utils/AI/AIInvestigationEngineRunLimits.test.ts +136 -0
  296. package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +15 -14
  297. package/Tests/Server/Utils/AI/AgentContextCompactor.test.ts +351 -0
  298. package/Tests/Server/Utils/AI/ChatAgentRunnerSharedThread.test.ts +842 -0
  299. package/Tests/Server/Utils/AI/ChatRunCancellation.test.ts +195 -0
  300. package/Tests/Server/Utils/AI/CodeFixReadiness.test.ts +10 -57
  301. package/Tests/Server/Utils/AI/FixRunBudget.test.ts +20 -24
  302. package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +7 -30
  303. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +225 -29
  304. package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +266 -25
  305. package/Tests/Server/Utils/AI/InvestigationThread.test.ts +580 -0
  306. package/Tests/Server/Utils/AI/InvestigationThreadService.test.ts +801 -0
  307. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +525 -43
  308. package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +4 -2
  309. package/Tests/Server/Utils/AI/LLMServiceUnreachableProvider.test.ts +435 -0
  310. package/Tests/Server/Utils/AI/ObservabilityAssistantLimits.test.ts +462 -0
  311. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +14 -25
  312. package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +2 -0
  313. package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +297 -15
  314. package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +10 -6
  315. package/Tests/Server/Utils/AI/ToolOutputPager.test.ts +382 -0
  316. package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +23 -4
  317. package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +25 -6
  318. package/Tests/Server/Utils/AI/Toolbox/Serializer.test.ts +14 -3
  319. package/Tests/Server/Utils/EditionEnforcement.test.ts +107 -147
  320. package/Tests/Server/Utils/{CommunityEditionSsoReport.test.ts → RelaxedScimTeamLocksReport.test.ts} +212 -208
  321. package/Tests/Server/Utils/SameOriginRequest.test.ts +372 -0
  322. package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +20 -0
  323. package/Tests/Server/Utils/Telemetry/MessageQueueDiscovery.test.ts +2590 -0
  324. package/Tests/Server/Utils/Telemetry/MessageQueueSpanQueryTwin.ts +246 -0
  325. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +23 -0
  326. package/Tests/Types/AI/AIAgentRunLimits.test.ts +255 -0
  327. package/Tests/Types/Code/JsonSyntax.test.ts +1300 -0
  328. package/Tests/Types/CustomField/CustomFieldCreateSettings.test.ts +1113 -0
  329. package/Tests/Types/CustomField/CustomFieldMappingCatalog.test.ts +91 -0
  330. package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +30 -0
  331. package/Tests/Types/Incident/IncidentFormIpAllowlist.test.ts +227 -0
  332. package/Tests/Types/Incident/IncidentFormPublic.test.ts +1916 -0
  333. package/Tests/Types/MessageQueue/MessageQueueEntityKey.test.ts +916 -0
  334. package/Tests/Types/MessageQueue/MessageQueueIdentity.test.ts +567 -0
  335. package/Tests/Types/MessageQueue/MessageQueueManualIdentity.test.ts +686 -0
  336. package/Tests/Types/MessageQueue/MessageQueueMetricCatalog.test.ts +1726 -0
  337. package/Tests/Types/MessageQueue/MessageQueueMetricResolution.test.ts +1758 -0
  338. package/Tests/Types/MessageQueue/MessagingInstrumentationFixtures.test.ts +4686 -0
  339. package/Tests/Types/MessageQueue/MessagingSystem.test.ts +840 -0
  340. package/Tests/Types/MessageQueue/MessagingTelemetryFixtures.ts +3287 -0
  341. package/Tests/Types/MessageQueue/MessagingTelemetryResolver.test.ts +2966 -0
  342. package/Tests/Types/Monitor/MessageQueueAlertTemplates.test.ts +5401 -0
  343. package/Tests/Types/Monitor/MetricEngineFold.ts +154 -0
  344. package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +5 -3
  345. package/Tests/Types/Team/ComplianceRule.test.ts +588 -37
  346. package/Tests/Types/Team/TeamComplianceStatus.test.ts +64 -11
  347. package/Tests/Types/XML.test.ts +51 -0
  348. package/Tests/UI/Components/CodeEditor/CodeEditor.test.tsx +2362 -0
  349. package/Tests/UI/Components/CodeEditor/CodeEditorCommands.test.ts +810 -0
  350. package/Tests/UI/Components/CodeEditor/CodeEditorHighlight.test.ts +338 -0
  351. package/Tests/UI/Components/CodeEditor/CodeEditorLanguages.test.ts +222 -0
  352. package/Tests/UI/Components/CodeEditor/CodeEditorStyles.test.ts +807 -0
  353. package/Tests/UI/Components/CodeEditor/NoMonaco.test.ts +245 -0
  354. package/Tests/UI/Components/CodeEditor/YamlEditor.test.tsx +729 -0
  355. package/Tests/UI/Components/CopyTextButton.test.tsx +76 -0
  356. package/Tests/UI/Components/EditionLabelLapseCopy.ts +51 -14
  357. package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +131 -23
  358. package/Tests/UI/Components/ErrorMessageAction.test.tsx +142 -0
  359. package/Tests/UI/Components/FacetSidebarInScope.test.tsx +459 -0
  360. package/Tests/UI/Components/FacetValuesInScope.test.ts +270 -0
  361. package/Tests/UI/Components/Forms/FormFieldCodeEditors.test.tsx +294 -0
  362. package/Tests/UI/Components/Forms/FormFieldMarkdown.test.tsx +126 -0
  363. package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +22 -75
  364. package/Tests/UI/Components/Forms/ModelFormOnBeforeCreateMiscData.test.tsx +254 -0
  365. package/Tests/UI/Components/Icon.test.tsx +32 -0
  366. package/Tests/UI/Components/Input.test.tsx +31 -0
  367. package/Tests/UI/Components/LockedFilterChipScope.test.tsx +501 -0
  368. package/Tests/UI/Components/MarkdownConverters.test.ts +253 -1
  369. package/Tests/UI/Components/MarkdownEditor.test.tsx +2785 -2
  370. package/Tests/UI/Components/MarkdownEditorHistory.test.ts +243 -0
  371. package/Tests/UI/Components/MarkdownListEditing.test.ts +1118 -0
  372. package/Tests/UI/Components/MarkdownPaste.test.ts +1180 -0
  373. package/Tests/UI/Components/MarkdownViewerCopy.test.tsx +466 -0
  374. package/Tests/UI/Components/MarkdownVisualEditing.test.ts +1089 -0
  375. package/Tests/UI/Components/ModelTable/BaseModelTableEmptyStateAction.test.tsx +623 -0
  376. package/Tests/UI/Components/ResetObjectID.test.tsx +282 -0
  377. package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
  378. package/Tests/UI/Components/Table/EmptyTableMessage.test.ts +439 -0
  379. package/Tests/UI/Components/Table/TableEmptyState.test.tsx +352 -0
  380. package/Tests/UI/Components/TableRowMobileTitles.test.tsx +78 -0
  381. package/Tests/UI/Components/TextArea.test.tsx +19 -0
  382. package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +12 -29
  383. package/Tests/UI/Components/fixtures/MarkdownPasteFixtures.ts +460 -0
  384. package/Tests/UI/EsbuildConfig.test.ts +115 -39
  385. package/Tests/UI/EsbuildEnterpriseAlias.test.ts +4 -4
  386. package/Tests/UI/Monitor/MonitorLogSummaryRender.test.tsx +377 -0
  387. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +81 -1
  388. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +17 -0
  389. package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
  390. package/Tests/Utils/Markdown/DashboardMarkdownRenderer.ts +81 -0
  391. package/Tests/Utils/Markdown/UntrustedMarkdown.test.ts +1339 -0
  392. package/Tests/Utils/Monitor/MonitorLogSummaryUtil.test.ts +862 -0
  393. package/Types/AI/AIAgentRunLimits.ts +146 -0
  394. package/Types/Code/CodeType.ts +2 -0
  395. package/Types/Code/JsonSyntax.ts +923 -0
  396. package/Types/CustomField/CustomFieldCreateSettings.ts +549 -0
  397. package/Types/CustomField/CustomFieldMappingCatalog.ts +77 -0
  398. package/Types/CustomField/CustomFieldValueValidator.ts +10 -7
  399. package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +1 -1
  400. package/Types/Icon/IconProp.ts +2 -0
  401. package/Types/Incident/IncidentFormIpAllowlist.ts +206 -0
  402. package/Types/Incident/IncidentFormPublic.ts +1145 -0
  403. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +10 -3
  404. package/Types/MessageQueue/MessageQueueIdentity.ts +256 -0
  405. package/Types/MessageQueue/MessageQueueManualIdentity.ts +325 -0
  406. package/Types/MessageQueue/MessageQueueMetricCatalog.ts +1357 -0
  407. package/Types/MessageQueue/MessagingSystem.ts +518 -0
  408. package/Types/MessageQueue/MessagingTelemetryResolver.ts +2311 -0
  409. package/Types/Monitor/MessageQueueAlertTemplates.ts +2128 -0
  410. package/Types/Permission.ts +310 -0
  411. package/Types/ResourceAiAgent/ResourceAiAccess.ts +10 -4
  412. package/Types/Rules/RuleCriteriaFieldRegistry.ts +12 -0
  413. package/Types/Rules/RuleRun.ts +4 -0
  414. package/Types/Team/ComplianceNotificationChannel.ts +4 -3
  415. package/Types/Team/ComplianceRule.ts +71 -18
  416. package/Types/Team/ComplianceRuleType.ts +2 -2
  417. package/Types/Team/TeamComplianceStatus.ts +6 -2
  418. package/Types/Telemetry/EntityType.ts +16 -0
  419. package/Types/Telemetry/LockedFilterDetail.ts +22 -0
  420. package/Types/XML.ts +26 -0
  421. package/UI/Components/CodeEditor/CodeEditor.css +379 -0
  422. package/UI/Components/CodeEditor/CodeEditor.tsx +1128 -294
  423. package/UI/Components/CodeEditor/CodeEditorCommands.ts +774 -0
  424. package/UI/Components/CodeEditor/CodeEditorHighlight.ts +106 -0
  425. package/UI/Components/CodeEditor/CodeEditorLanguages.ts +164 -0
  426. package/UI/Components/CodeEditor/YamlEditor.tsx +28 -326
  427. package/UI/Components/CopyTextButton/CopyTextButton.tsx +5 -1
  428. package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +6 -25
  429. package/UI/Components/EditionLabel/EditionLabel.tsx +14 -13
  430. package/UI/Components/ErrorMessage/ErrorMessage.tsx +11 -1
  431. package/UI/Components/Forms/Fields/FormField.tsx +8 -0
  432. package/UI/Components/Forms/Types/Field.ts +8 -0
  433. package/UI/Components/Icon/Icon.tsx +18 -0
  434. package/UI/Components/Input/Input.tsx +11 -4
  435. package/UI/Components/LogsViewer/LogsViewer.tsx +4 -0
  436. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +21 -3
  437. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  438. package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +877 -79
  439. package/UI/Components/Markdown.tsx/MarkdownEditorHistory.ts +284 -0
  440. package/UI/Components/Markdown.tsx/MarkdownListEditing.ts +1362 -0
  441. package/UI/Components/Markdown.tsx/MarkdownPaste.ts +1326 -0
  442. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +106 -10
  443. package/UI/Components/Markdown.tsx/MarkdownVisualEditing.ts +1032 -0
  444. package/UI/Components/ModelTable/BaseModelTable.tsx +85 -7
  445. package/UI/Components/ResetObjectID/ResetObjectID.tsx +35 -9
  446. package/UI/Components/Table/EmptyTableMessage.ts +81 -0
  447. package/UI/Components/Table/Table.tsx +50 -21
  448. package/UI/Components/Table/TableRow.tsx +9 -1
  449. package/UI/Components/TelemetryViewer/FacetVisibility.ts +97 -2
  450. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +13 -0
  451. package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +214 -7
  452. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +21 -3
  453. package/UI/Components/TextArea/TextArea.tsx +9 -2
  454. package/UI/Components/Workflow/ModelFieldPicker.tsx +10 -12
  455. package/UI/Styles/Theme.css +8 -0
  456. package/UI/esbuild-config.js +2 -32
  457. package/Utils/LabelRuleImportExport.ts +2 -0
  458. package/Utils/Markdown/UntrustedMarkdown.ts +1513 -0
  459. package/Utils/Monitor/IncomingEmailMonitorRequestUtil.ts +41 -0
  460. package/Utils/Monitor/MonitorLogSummaryUtil.ts +212 -0
  461. package/Utils/Telemetry/EntityKey.ts +48 -0
  462. package/build/dist/Models/DatabaseModels/AIConversation.js +125 -1
  463. package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
  464. package/build/dist/Models/DatabaseModels/CephCluster.js +12 -0
  465. package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
  466. package/build/dist/Models/DatabaseModels/CloudResource.js +12 -0
  467. package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
  468. package/build/dist/Models/DatabaseModels/DatabaseServer.js +12 -0
  469. package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
  470. package/build/dist/Models/DatabaseModels/DockerHost.js +12 -0
  471. package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
  472. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +12 -0
  473. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
  474. package/build/dist/Models/DatabaseModels/GlobalOidc.js +0 -4
  475. package/build/dist/Models/DatabaseModels/GlobalOidc.js.map +1 -1
  476. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +0 -4
  477. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
  478. package/build/dist/Models/DatabaseModels/GlobalSso.js +0 -4
  479. package/build/dist/Models/DatabaseModels/GlobalSso.js.map +1 -1
  480. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +0 -4
  481. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
  482. package/build/dist/Models/DatabaseModels/Host.js +12 -0
  483. package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
  484. package/build/dist/Models/DatabaseModels/IncidentForm.js +605 -0
  485. package/build/dist/Models/DatabaseModels/IncidentForm.js.map +1 -0
  486. package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js +299 -0
  487. package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js.map +1 -0
  488. package/build/dist/Models/DatabaseModels/IncidentTemplate.js +60 -0
  489. package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
  490. package/build/dist/Models/DatabaseModels/Index.js +14 -0
  491. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  492. package/build/dist/Models/DatabaseModels/IoTFleet.js +12 -0
  493. package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
  494. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +12 -0
  495. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  496. package/build/dist/Models/DatabaseModels/MessageQueue.js +1106 -0
  497. package/build/dist/Models/DatabaseModels/MessageQueue.js.map +1 -0
  498. package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js +562 -0
  499. package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js.map +1 -0
  500. package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js +643 -0
  501. package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js.map +1 -0
  502. package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js +506 -0
  503. package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js.map +1 -0
  504. package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js +505 -0
  505. package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js.map +1 -0
  506. package/build/dist/Models/DatabaseModels/PodmanHost.js +12 -0
  507. package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
  508. package/build/dist/Models/DatabaseModels/Project.js +61 -0
  509. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  510. package/build/dist/Models/DatabaseModels/ProjectOidc.js +0 -4
  511. package/build/dist/Models/DatabaseModels/ProjectOidc.js.map +1 -1
  512. package/build/dist/Models/DatabaseModels/ProjectSso.js +0 -4
  513. package/build/dist/Models/DatabaseModels/ProjectSso.js.map +1 -1
  514. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +12 -0
  515. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
  516. package/build/dist/Models/DatabaseModels/RumApplication.js +12 -0
  517. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  518. package/build/dist/Models/DatabaseModels/ServerlessFunction.js +12 -0
  519. package/build/dist/Models/DatabaseModels/ServerlessFunction.js.map +1 -1
  520. package/build/dist/Models/DatabaseModels/Service.js +12 -0
  521. package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
  522. package/build/dist/Models/DatabaseModels/StatusPageOidc.js +0 -4
  523. package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
  524. package/build/dist/Models/DatabaseModels/StatusPageSso.js +0 -4
  525. package/build/dist/Models/DatabaseModels/StatusPageSso.js.map +1 -1
  526. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +49 -3
  527. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
  528. package/build/dist/Models/DatabaseModels/VMwareVCenter.js +12 -0
  529. package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
  530. package/build/dist/Server/API/AIChatAPI.js +9 -103
  531. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  532. package/build/dist/Server/API/AIInvestigationAPI.js +3 -3
  533. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  534. package/build/dist/Server/API/AIInvestigationConversationAPI.js +162 -0
  535. package/build/dist/Server/API/AIInvestigationConversationAPI.js.map +1 -0
  536. package/build/dist/Server/API/IncidentFormAPI.js +245 -0
  537. package/build/dist/Server/API/IncidentFormAPI.js.map +1 -0
  538. package/build/dist/Server/API/ProjectOIDC.js +16 -24
  539. package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
  540. package/build/dist/Server/API/ProjectSSO.js +16 -24
  541. package/build/dist/Server/API/ProjectSSO.js.map +1 -1
  542. package/build/dist/Server/API/StatusPageAPI.js +42 -75
  543. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  544. package/build/dist/Server/Enterprise/EnterpriseEdition.js +79 -25
  545. package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -1
  546. package/build/dist/Server/Enterprise/EnterpriseFeature.js +24 -3
  547. package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -1
  548. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +1 -1
  549. package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -1
  550. package/build/dist/Server/EnvironmentConfig.js +1 -1
  551. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js +63 -0
  552. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js.map +1 -0
  553. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js +41 -0
  554. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js.map +1 -0
  555. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js +40 -0
  556. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js.map +1 -0
  557. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js +183 -0
  558. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js.map +1 -0
  559. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  560. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  561. package/build/dist/Server/Middleware/IncidentFormRateLimit.js +559 -0
  562. package/build/dist/Server/Middleware/IncidentFormRateLimit.js.map +1 -0
  563. package/build/dist/Server/Middleware/UserAuthorization.js +11 -30
  564. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  565. package/build/dist/Server/Services/AIService.js +10 -13
  566. package/build/dist/Server/Services/AIService.js.map +1 -1
  567. package/build/dist/Server/Services/IncidentCustomFieldService.js +83 -0
  568. package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
  569. package/build/dist/Server/Services/IncidentEpisodeMemberService.js +8 -4
  570. package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
  571. package/build/dist/Server/Services/IncidentEpisodeService.js +10 -1
  572. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  573. package/build/dist/Server/Services/IncidentFormService.js +1202 -0
  574. package/build/dist/Server/Services/IncidentFormService.js.map +1 -0
  575. package/build/dist/Server/Services/IncidentFormSubmissionService.js +91 -0
  576. package/build/dist/Server/Services/IncidentFormSubmissionService.js.map +1 -0
  577. package/build/dist/Server/Services/IncidentGroupingEngineService.js +20 -8
  578. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  579. package/build/dist/Server/Services/IncidentService.js +106 -4
  580. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  581. package/build/dist/Server/Services/IncidentTemplateService.js +18 -0
  582. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  583. package/build/dist/Server/Services/Index.js +14 -0
  584. package/build/dist/Server/Services/Index.js.map +1 -1
  585. package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js +262 -0
  586. package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js.map +1 -0
  587. package/build/dist/Server/Services/MessageQueueLabelRuleService.js +13 -0
  588. package/build/dist/Server/Services/MessageQueueLabelRuleService.js.map +1 -0
  589. package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js +334 -0
  590. package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js.map +1 -0
  591. package/build/dist/Server/Services/MessageQueueOwnerRuleService.js +13 -0
  592. package/build/dist/Server/Services/MessageQueueOwnerRuleService.js.map +1 -0
  593. package/build/dist/Server/Services/MessageQueueOwnerTeamService.js +62 -0
  594. package/build/dist/Server/Services/MessageQueueOwnerTeamService.js.map +1 -0
  595. package/build/dist/Server/Services/MessageQueueOwnerUserService.js +62 -0
  596. package/build/dist/Server/Services/MessageQueueOwnerUserService.js.map +1 -0
  597. package/build/dist/Server/Services/MessageQueueService.js +1140 -0
  598. package/build/dist/Server/Services/MessageQueueService.js.map +1 -0
  599. package/build/dist/Server/Services/OpenTelemetryIngestService.js +25 -4
  600. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  601. package/build/dist/Server/Services/ProjectService.js +0 -54
  602. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  603. package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -4
  604. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  605. package/build/dist/Server/Services/StatusPageService.js +0 -46
  606. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  607. package/build/dist/Server/Services/TeamComplianceSettingService.js +120 -31
  608. package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
  609. package/build/dist/Server/Services/UserNotificationRuleService.js +8 -1
  610. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  611. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +12 -3
  612. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
  613. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +6 -3
  614. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  615. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +109 -37
  616. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
  617. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +16 -0
  618. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
  619. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +3 -3
  620. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +9 -6
  621. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
  622. package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js +128 -0
  623. package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js.map +1 -0
  624. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +192 -50
  625. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
  626. package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js +133 -0
  627. package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js.map +1 -0
  628. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +53 -13
  629. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  630. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
  631. package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js +206 -0
  632. package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js.map +1 -0
  633. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +53 -26
  634. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
  635. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +4 -2
  636. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
  637. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +1 -29
  638. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
  639. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +24 -24
  640. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
  641. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +7 -2
  642. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  643. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +29 -14
  644. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  645. package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +38 -14
  646. package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -1
  647. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +2 -0
  648. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -1
  649. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +40 -10
  650. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  651. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +19 -7
  652. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  653. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +19 -7
  654. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  655. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +15 -18
  656. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
  657. package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js +412 -0
  658. package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js.map +1 -0
  659. package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js +495 -0
  660. package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js.map +1 -0
  661. package/build/dist/Server/Utils/AI/Toolbox/Serializer.js +10 -3
  662. package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -1
  663. package/build/dist/Server/Utils/EditionEnforcement.js +20 -158
  664. package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -1
  665. package/build/dist/Server/Utils/LLM/LLMService.js +193 -54
  666. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  667. package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js +160 -0
  668. package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js.map +1 -0
  669. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
  670. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
  671. package/build/dist/Server/Utils/SameOriginRequest.js +151 -0
  672. package/build/dist/Server/Utils/SameOriginRequest.js.map +1 -0
  673. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
  674. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
  675. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +4 -1
  676. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  677. package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js +1015 -0
  678. package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js.map +1 -0
  679. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +4 -1
  680. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
  681. package/build/dist/Server/Utils/VendorAssets.js +3 -3
  682. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +3 -1
  683. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  684. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +3 -1
  685. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
  686. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +10 -1
  687. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  688. package/build/dist/Types/AI/AIAgentRunLimits.js +91 -0
  689. package/build/dist/Types/AI/AIAgentRunLimits.js.map +1 -0
  690. package/build/dist/Types/Code/CodeType.js +2 -0
  691. package/build/dist/Types/Code/CodeType.js.map +1 -1
  692. package/build/dist/Types/Code/JsonSyntax.js +604 -0
  693. package/build/dist/Types/Code/JsonSyntax.js.map +1 -0
  694. package/build/dist/Types/CustomField/CustomFieldCreateSettings.js +310 -0
  695. package/build/dist/Types/CustomField/CustomFieldCreateSettings.js.map +1 -0
  696. package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js +38 -0
  697. package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js.map +1 -1
  698. package/build/dist/Types/CustomField/CustomFieldValueValidator.js +10 -7
  699. package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -1
  700. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +1 -1
  701. package/build/dist/Types/Icon/IconProp.js +2 -0
  702. package/build/dist/Types/Icon/IconProp.js.map +1 -1
  703. package/build/dist/Types/Incident/IncidentFormIpAllowlist.js +119 -0
  704. package/build/dist/Types/Incident/IncidentFormIpAllowlist.js.map +1 -0
  705. package/build/dist/Types/Incident/IncidentFormPublic.js +678 -0
  706. package/build/dist/Types/Incident/IncidentFormPublic.js.map +1 -0
  707. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +10 -3
  708. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
  709. package/build/dist/Types/MessageQueue/MessageQueueIdentity.js +184 -0
  710. package/build/dist/Types/MessageQueue/MessageQueueIdentity.js.map +1 -0
  711. package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js +190 -0
  712. package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js.map +1 -0
  713. package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js +1057 -0
  714. package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js.map +1 -0
  715. package/build/dist/Types/MessageQueue/MessagingSystem.js +394 -0
  716. package/build/dist/Types/MessageQueue/MessagingSystem.js.map +1 -0
  717. package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js +1768 -0
  718. package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js.map +1 -0
  719. package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js +1207 -0
  720. package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js.map +1 -0
  721. package/build/dist/Types/Permission.js +274 -0
  722. package/build/dist/Types/Permission.js.map +1 -1
  723. package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +10 -4
  724. package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -1
  725. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +12 -0
  726. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  727. package/build/dist/Types/Rules/RuleRun.js +4 -0
  728. package/build/dist/Types/Rules/RuleRun.js.map +1 -1
  729. package/build/dist/Types/Team/ComplianceNotificationChannel.js +4 -3
  730. package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -1
  731. package/build/dist/Types/Team/ComplianceRule.js +48 -14
  732. package/build/dist/Types/Team/ComplianceRule.js.map +1 -1
  733. package/build/dist/Types/Team/ComplianceRuleType.js +2 -2
  734. package/build/dist/Types/Telemetry/EntityType.js +16 -0
  735. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  736. package/build/dist/Types/XML.js +23 -0
  737. package/build/dist/Types/XML.js.map +1 -1
  738. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +641 -266
  739. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  740. package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js +464 -0
  741. package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js.map +1 -0
  742. package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js +69 -0
  743. package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js.map +1 -0
  744. package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js +108 -0
  745. package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js.map +1 -0
  746. package/build/dist/UI/Components/CodeEditor/YamlEditor.js +14 -186
  747. package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -1
  748. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +5 -1
  749. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
  750. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +5 -14
  751. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -1
  752. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +14 -13
  753. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  754. package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js +1 -1
  755. package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js.map +1 -1
  756. package/build/dist/UI/Components/Forms/Fields/FormField.js +8 -3
  757. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  758. package/build/dist/UI/Components/Icon/Icon.js +8 -0
  759. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  760. package/build/dist/UI/Components/Input/Input.js +11 -3
  761. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  762. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +2 -1
  763. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  764. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +14 -4
  765. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  766. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  767. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  768. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +710 -65
  769. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
  770. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js +238 -0
  771. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js.map +1 -0
  772. package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js +1028 -0
  773. package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js.map +1 -0
  774. package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js +1088 -0
  775. package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js.map +1 -0
  776. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +70 -10
  777. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  778. package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js +887 -0
  779. package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js.map +1 -0
  780. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +57 -6
  781. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  782. package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js +7 -5
  783. package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js.map +1 -1
  784. package/build/dist/UI/Components/Table/EmptyTableMessage.js +51 -0
  785. package/build/dist/UI/Components/Table/EmptyTableMessage.js.map +1 -0
  786. package/build/dist/UI/Components/Table/Table.js +30 -11
  787. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  788. package/build/dist/UI/Components/Table/TableRow.js +9 -2
  789. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  790. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js +63 -1
  791. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js.map +1 -1
  792. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +4 -2
  793. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  794. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +91 -10
  795. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -1
  796. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +14 -4
  797. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  798. package/build/dist/UI/Components/TextArea/TextArea.js +9 -3
  799. package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
  800. package/build/dist/UI/Components/Workflow/ModelFieldPicker.js +1 -2
  801. package/build/dist/UI/Components/Workflow/ModelFieldPicker.js.map +1 -1
  802. package/build/dist/Utils/LabelRuleImportExport.js +2 -0
  803. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
  804. package/build/dist/Utils/Markdown/UntrustedMarkdown.js +988 -0
  805. package/build/dist/Utils/Markdown/UntrustedMarkdown.js.map +1 -0
  806. package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js +34 -0
  807. package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js.map +1 -0
  808. package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js +148 -0
  809. package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js.map +1 -0
  810. package/build/dist/Utils/Telemetry/EntityKey.js +36 -0
  811. package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
  812. package/package.json +1 -3
  813. package/Server/Utils/CommunityEditionSsoReport.ts +0 -361
  814. package/Tests/App/Accounts/LoginSsoEdition.test.tsx +0 -616
  815. package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +0 -217
  816. package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +0 -271
  817. package/Tests/App/Dashboard/CloudDocumentationMarkdown.test.ts +0 -215
  818. package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +0 -784
  819. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +0 -518
  820. package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +0 -419
  821. package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +0 -769
  822. package/Tests/UI/Components/CodeEditor.test.tsx +0 -638
  823. package/Tests/UI/Components/MonacoLoader.test.ts +0 -67
  824. package/Tests/UI/Components/MonacoRuntime.test.ts +0 -121
  825. package/Tests/UI/Components/Telemetry/TelemetryRetentionConfigForm.test.tsx +0 -329
  826. package/Tests/UI/Components/YamlEditor.test.tsx +0 -847
  827. package/UI/Components/CodeEditor/MonacoLoader.ts +0 -36
  828. package/UI/Components/Telemetry/TelemetryRetentionConfigForm.tsx +0 -319
  829. package/UI/Components/Telemetry/TelemetryRetentionConfigSummary.tsx +0 -223
  830. package/build/dist/Server/Utils/CommunityEditionSsoReport.js +0 -223
  831. package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +0 -1
  832. package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +0 -34
  833. package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +0 -1
  834. package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js +0 -171
  835. package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js.map +0 -1
  836. package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js +0 -119
  837. package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js.map +0 -1
@@ -0,0 +1,1260 @@
1
+ import {
2
+ afterEach,
3
+ beforeEach,
4
+ describe,
5
+ expect,
6
+ jest,
7
+ test,
8
+ } from "@jest/globals";
9
+
10
+ /*
11
+ * What a stranger holding a form's link is told about the form
12
+ * (IncidentFormService.getPublicForm), and the checks that decide whether
13
+ * they are told anything at all - the same checks every submission passes
14
+ * first:
15
+ *
16
+ * - the link has a share key's shape, before anything reaches Postgres;
17
+ * - a form holds that key, and it is switched on;
18
+ * - the form's project is on a plan that includes forms (billing on);
19
+ * - the visitor's network is on the form's IP allowlist, if it has one.
20
+ *
21
+ * An unusable form of any kind - junk link, unknown link, form turned off,
22
+ * project off plan - gets one and the same 404, so trying links tells a
23
+ * prober nothing. Only the network check, reached for a form that exists and
24
+ * is on, says what is wrong (403).
25
+ *
26
+ * The answer itself is the safe subset: never the form's id, project,
27
+ * template, link key, allowlist or success message, never a custom field the
28
+ * form does not ask, and severities only when the reporter may choose one.
29
+ *
30
+ * Only the database reads are stubbed; the checks and the shaping run for
31
+ * real. Billing is switched through a mocked EnvironmentConfig, as the
32
+ * on-call calendar feed's plan check is tested.
33
+ */
34
+
35
+ type EnvMockGlobal = typeof globalThis & {
36
+ __oneuptimeIncidentFormBillingEnabled: boolean;
37
+ };
38
+
39
+ jest.mock("../../../Server/EnvironmentConfig", () => {
40
+ const actual: Record<string, unknown> = jest.requireActual(
41
+ "../../../Server/EnvironmentConfig",
42
+ ) as Record<string, unknown>;
43
+ const mocked: Record<string, unknown> = { ...actual };
44
+ const mockGlobal: EnvMockGlobal = globalThis as EnvMockGlobal;
45
+
46
+ mockGlobal.__oneuptimeIncidentFormBillingEnabled = false;
47
+
48
+ Object.defineProperty(mocked, "IsBillingEnabled", {
49
+ configurable: true,
50
+ enumerable: true,
51
+ get: (): boolean => {
52
+ return mockGlobal.__oneuptimeIncidentFormBillingEnabled;
53
+ },
54
+ });
55
+
56
+ return mocked;
57
+ });
58
+
59
+ jest.mock("../../../Server/Utils/Logger", () => {
60
+ return {
61
+ __esModule: true,
62
+ default: {
63
+ debug: jest.fn(),
64
+ info: jest.fn(),
65
+ warn: jest.fn(),
66
+ error: jest.fn(),
67
+ },
68
+ getLogAttributesFromRequest: jest.fn().mockReturnValue({}),
69
+ };
70
+ });
71
+
72
+ import fs from "fs";
73
+ import path from "path";
74
+ import IncidentCustomField from "../../../Models/DatabaseModels/IncidentCustomField";
75
+ import IncidentForm from "../../../Models/DatabaseModels/IncidentForm";
76
+ import IncidentSeverity from "../../../Models/DatabaseModels/IncidentSeverity";
77
+ import IncidentTemplate from "../../../Models/DatabaseModels/IncidentTemplate";
78
+ import Monitor from "../../../Models/DatabaseModels/Monitor";
79
+ import IncidentCustomFieldService from "../../../Server/Services/IncidentCustomFieldService";
80
+ import IncidentFormService, {
81
+ INCIDENT_FORM_NETWORK_NOT_ALLOWED_MESSAGE,
82
+ INCIDENT_FORM_NOT_AVAILABLE_MESSAGE,
83
+ } from "../../../Server/Services/IncidentFormService";
84
+ import IncidentService from "../../../Server/Services/IncidentService";
85
+ import IncidentSeverityService from "../../../Server/Services/IncidentSeverityService";
86
+ import IncidentTemplateService from "../../../Server/Services/IncidentTemplateService";
87
+ import ProjectService from "../../../Server/Services/ProjectService";
88
+ import CaptchaUtil from "../../../Server/Utils/Captcha";
89
+ import {
90
+ ClientIpRequestLike,
91
+ resolveClientIp,
92
+ } from "../../../Server/Utils/ClientIp";
93
+ import logger from "../../../Server/Utils/Logger";
94
+ import SortOrder from "../../../Types/BaseDatabase/SortOrder";
95
+ import SubscriptionPlan, {
96
+ PlanType,
97
+ } from "../../../Types/Billing/SubscriptionPlan";
98
+ import Color from "../../../Types/Color";
99
+ import CustomFieldMappingSourceResource from "../../../Types/CustomField/CustomFieldMappingSourceResource";
100
+ import CustomFieldType from "../../../Types/CustomField/CustomFieldType";
101
+ import Exception from "../../../Types/Exception/Exception";
102
+ import ExceptionCode from "../../../Types/Exception/ExceptionCode";
103
+ import ForbiddenException from "../../../Types/Exception/ForbiddenException";
104
+ import NotFoundException from "../../../Types/Exception/NotFoundException";
105
+ import {
106
+ IncidentFormFieldSetting,
107
+ PublicIncidentForm,
108
+ } from "../../../Types/Incident/IncidentFormPublic";
109
+ import ObjectID from "../../../Types/ObjectID";
110
+
111
+ type MockedFn = ReturnType<typeof jest.fn>;
112
+
113
+ const PROJECT_ID: ObjectID = new ObjectID(
114
+ "5d7f3c0a-4c55-4d3e-9a3e-2d4a7d1c9f01",
115
+ );
116
+ const FORM_ID: string = "a1b2c3d4-0000-4000-8000-0000000000f1";
117
+ const SHARE_KEY: string = "7c9e6679-7425-40de-944b-e07fc1f90ae7";
118
+ const OTHER_SHARE_KEY: string = "0f8fad5b-d9cb-469f-a165-70867728950e";
119
+ const CRITICAL_SEVERITY_ID: string = "b0000000-0000-4000-8000-0000000000f1";
120
+ const MINOR_SEVERITY_ID: string = "b0000000-0000-4000-8000-0000000000f2";
121
+ const UNCOLOURED_SEVERITY_ID: string = "b0000000-0000-4000-8000-0000000000f3";
122
+ const TEMPLATE_ID: string = "c0000000-0000-4000-8000-0000000000f1";
123
+ const CLIENT_IP: string = "203.0.113.7";
124
+
125
+ function setBillingEnabled(value: boolean): void {
126
+ (globalThis as EnvMockGlobal).__oneuptimeIncidentFormBillingEnabled = value;
127
+ }
128
+
129
+ // A column read back from Postgres as NULL, which the model's types omit.
130
+ function setNull(form: IncidentForm, column: string): void {
131
+ (form as unknown as Record<string, unknown>)[column] = null;
132
+ }
133
+
134
+ function buildForm(data: Partial<IncidentForm> = {}): IncidentForm {
135
+ const form: IncidentForm = new IncidentForm();
136
+ form._id = FORM_ID;
137
+ form.projectId = PROJECT_ID;
138
+ form.name = "Report a Problem";
139
+ form.description = "Tell us **what** is broken.";
140
+ form.isEnabled = true;
141
+ form.shareKey = new ObjectID(SHARE_KEY);
142
+ form.incidentSeverityId = new ObjectID(CRITICAL_SEVERITY_ID);
143
+ form.allowReporterToChooseSeverity = false;
144
+ form.incidentTemplateId = new ObjectID(TEMPLATE_ID);
145
+ form.descriptionSetting = IncidentFormFieldSetting.Optional;
146
+ form.customFieldSettings = {};
147
+ form.isReporterDetailsRequired = true;
148
+ form.successMessage = "Thanks - we are on it.";
149
+ form.ipWhitelist = "";
150
+ Object.assign(form, data);
151
+ return form;
152
+ }
153
+
154
+ function customField(data: {
155
+ name: string;
156
+ variableKey: string;
157
+ customFieldType: CustomFieldType;
158
+ sortOrder: number;
159
+ dropdownOptions?: string;
160
+ description?: string;
161
+ }): IncidentCustomField {
162
+ const field: IncidentCustomField = new IncidentCustomField();
163
+ field._id = ObjectID.generate().toString();
164
+ field.projectId = PROJECT_ID;
165
+ field.name = data.name;
166
+ field.variableKey = data.variableKey;
167
+ field.customFieldType = data.customFieldType;
168
+ field.sortOrder = data.sortOrder;
169
+
170
+ if (data.dropdownOptions !== undefined) {
171
+ field.dropdownOptions = data.dropdownOptions;
172
+ }
173
+
174
+ if (data.description !== undefined) {
175
+ field.description = data.description;
176
+ }
177
+
178
+ /*
179
+ * The project-wide switches say "show and require everything": the form's
180
+ * own settings, not these, decide what it asks.
181
+ */
182
+ field.showOnCreate = true;
183
+ field.isRequiredOnCreate = true;
184
+
185
+ return field;
186
+ }
187
+
188
+ const PROJECT_FIELDS: Array<IncidentCustomField> = [
189
+ customField({
190
+ name: "Impact",
191
+ variableKey: "impact",
192
+ customFieldType: CustomFieldType.Dropdown,
193
+ sortOrder: 2,
194
+ dropdownOptions: "Low\nHigh",
195
+ description: "How bad is it?",
196
+ }),
197
+ customField({
198
+ name: "Affected Location",
199
+ variableKey: "affected_location",
200
+ customFieldType: CustomFieldType.Text,
201
+ sortOrder: 1,
202
+ dropdownOptions: "should never be sent for a text field",
203
+ }),
204
+ customField({
205
+ name: "Internal Notes",
206
+ variableKey: "internal_notes",
207
+ customFieldType: CustomFieldType.LongText,
208
+ sortOrder: 3,
209
+ }),
210
+ customField({
211
+ name: "Cost Center",
212
+ variableKey: "cost_center",
213
+ customFieldType: CustomFieldType.Text,
214
+ sortOrder: 4,
215
+ }),
216
+ customField({
217
+ name: "Legal Hold",
218
+ variableKey: "legal_hold",
219
+ customFieldType: CustomFieldType.Boolean,
220
+ sortOrder: 5,
221
+ }),
222
+ ];
223
+
224
+ // Asks two fields; hides one, leaves one on Default and never names one.
225
+ const FORM_QUESTIONS: Record<string, string> = {
226
+ impact: "Required",
227
+ affected_location: "Optional",
228
+ internal_notes: "Hidden",
229
+ cost_center: "Default",
230
+ };
231
+
232
+ function severity(data: {
233
+ id: string;
234
+ name: string;
235
+ order: number;
236
+ color?: string;
237
+ }): IncidentSeverity {
238
+ const row: IncidentSeverity = new IncidentSeverity();
239
+ row._id = data.id;
240
+ row.name = data.name;
241
+ row.order = data.order;
242
+ row.projectId = PROJECT_ID;
243
+
244
+ if (data.color) {
245
+ row.color = new Color(data.color);
246
+ }
247
+
248
+ return row;
249
+ }
250
+
251
+ const PROJECT_SEVERITIES: Array<IncidentSeverity> = [
252
+ severity({
253
+ id: CRITICAL_SEVERITY_ID,
254
+ name: "Critical",
255
+ order: 1,
256
+ color: "#ff0000",
257
+ }),
258
+ severity({
259
+ id: MINOR_SEVERITY_ID,
260
+ name: "Minor",
261
+ order: 2,
262
+ color: "#00ff00",
263
+ }),
264
+ severity({ id: UNCOLOURED_SEVERITY_ID, name: "Unsorted", order: 3 }),
265
+ ];
266
+
267
+ let storedForm: IncidentForm | null = null;
268
+ let formFindOneBy: MockedFn;
269
+ let customFieldFindBy: MockedFn;
270
+ let severityFindBy: MockedFn;
271
+ let captchaEnabled: MockedFn;
272
+
273
+ beforeEach(() => {
274
+ setBillingEnabled(false);
275
+ storedForm = buildForm();
276
+
277
+ formFindOneBy = jest
278
+ .spyOn(IncidentFormService, "findOneBy")
279
+ .mockImplementation((async (findBy: {
280
+ query: { shareKey?: ObjectID };
281
+ }): Promise<IncidentForm | null> => {
282
+ if (
283
+ storedForm &&
284
+ findBy.query.shareKey?.toString() === storedForm.shareKey?.toString()
285
+ ) {
286
+ return storedForm;
287
+ }
288
+
289
+ return null;
290
+ }) as never) as unknown as MockedFn;
291
+
292
+ customFieldFindBy = jest
293
+ .spyOn(IncidentCustomFieldService, "findBy")
294
+ .mockResolvedValue(PROJECT_FIELDS as never) as unknown as MockedFn;
295
+
296
+ severityFindBy = jest
297
+ .spyOn(IncidentSeverityService, "findBy")
298
+ .mockResolvedValue(PROJECT_SEVERITIES as never) as unknown as MockedFn;
299
+
300
+ captchaEnabled = jest
301
+ .spyOn(CaptchaUtil, "isCaptchaEnabled")
302
+ .mockReturnValue(false) as unknown as MockedFn;
303
+ });
304
+
305
+ afterEach(() => {
306
+ jest.restoreAllMocks();
307
+ jest.clearAllMocks();
308
+ });
309
+
310
+ function getPublicForm(
311
+ data: { shareKey?: string | undefined; clientIp?: string | undefined } = {},
312
+ ): Promise<PublicIncidentForm> {
313
+ return IncidentFormService.getPublicForm({
314
+ shareKey: "shareKey" in data ? data.shareKey : SHARE_KEY,
315
+ clientIp: "clientIp" in data ? data.clientIp : CLIENT_IP,
316
+ });
317
+ }
318
+
319
+ async function refusal(
320
+ promise: Promise<unknown>,
321
+ ): Promise<Exception | undefined> {
322
+ try {
323
+ await promise;
324
+ } catch (err) {
325
+ return err as Exception;
326
+ }
327
+
328
+ return undefined;
329
+ }
330
+
331
+ describe("IncidentFormService.getPublicForm - what a visitor is told", () => {
332
+ test("a plain form: its name, description and question settings, and nothing else", async () => {
333
+ const form: PublicIncidentForm = await getPublicForm();
334
+
335
+ expect(form).toEqual({
336
+ name: "Report a Problem",
337
+ description: "Tell us **what** is broken.",
338
+ descriptionSetting: IncidentFormFieldSetting.Optional,
339
+ isReporterDetailsRequired: true,
340
+ customFields: [],
341
+ isCaptchaRequired: false,
342
+ });
343
+ });
344
+
345
+ test("looks the form up by its share key alone, as root, reading only the columns it needs", async () => {
346
+ await getPublicForm();
347
+
348
+ expect(formFindOneBy).toHaveBeenCalledTimes(1);
349
+
350
+ const findBy: {
351
+ query: Record<string, unknown>;
352
+ select: Record<string, unknown>;
353
+ props: Record<string, unknown>;
354
+ } = formFindOneBy.mock.calls[0]![0] as never;
355
+
356
+ expect(Object.keys(findBy.query)).toEqual(["shareKey"]);
357
+ expect(findBy.query["shareKey"]).toBeInstanceOf(ObjectID);
358
+ expect(String(findBy.query["shareKey"])).toBe(SHARE_KEY);
359
+ expect(findBy.props).toEqual({ isRoot: true });
360
+ expect(findBy.select).toEqual({
361
+ _id: true,
362
+ projectId: true,
363
+ name: true,
364
+ description: true,
365
+ isEnabled: true,
366
+ incidentSeverityId: true,
367
+ allowReporterToChooseSeverity: true,
368
+ incidentTemplateId: true,
369
+ descriptionSetting: true,
370
+ customFieldSettings: true,
371
+ isReporterDetailsRequired: true,
372
+ successMessage: true,
373
+ ipWhitelist: true,
374
+ });
375
+ });
376
+
377
+ test("finds the form whatever the case of the key in the link, or stray spaces around it", async () => {
378
+ await getPublicForm({ shareKey: ` ${SHARE_KEY.toUpperCase()} ` });
379
+
380
+ const findBy: { query: Record<string, unknown> } = formFindOneBy.mock
381
+ .calls[0]![0] as never;
382
+
383
+ expect(String(findBy.query["shareKey"])).toBe(SHARE_KEY);
384
+ });
385
+
386
+ test("never sends the form's id, project, template, link key, allowlist or success message", async () => {
387
+ storedForm = buildForm({
388
+ allowReporterToChooseSeverity: true,
389
+ customFieldSettings: FORM_QUESTIONS,
390
+ ipWhitelist: `${CLIENT_IP}\n198.51.100.0/24`,
391
+ });
392
+
393
+ const sent: string = JSON.stringify(await getPublicForm());
394
+
395
+ for (const secret of [
396
+ FORM_ID,
397
+ PROJECT_ID.toString(),
398
+ TEMPLATE_ID,
399
+ SHARE_KEY,
400
+ "198.51.100.0/24",
401
+ "Thanks - we are on it.",
402
+ "impact",
403
+ "affected_location",
404
+ "internal_notes",
405
+ "Internal Notes",
406
+ "Cost Center",
407
+ "Legal Hold",
408
+ ]) {
409
+ expect(sent).not.toContain(secret);
410
+ }
411
+ });
412
+
413
+ test("leaves out a blank description", async () => {
414
+ storedForm = buildForm({ description: " " });
415
+
416
+ expect(await getPublicForm()).not.toHaveProperty("description");
417
+ });
418
+
419
+ test.each([
420
+ [IncidentFormFieldSetting.Required],
421
+ [IncidentFormFieldSetting.Hidden],
422
+ ])(
423
+ "passes on a %s description question",
424
+ async (setting: IncidentFormFieldSetting) => {
425
+ storedForm = buildForm({ descriptionSetting: setting });
426
+
427
+ expect((await getPublicForm()).descriptionSetting).toBe(setting);
428
+ },
429
+ );
430
+
431
+ test("says when the reporter's details are optional", async () => {
432
+ storedForm = buildForm({ isReporterDetailsRequired: false });
433
+
434
+ expect((await getPublicForm()).isReporterDetailsRequired).toBe(false);
435
+ });
436
+
437
+ test("asks for the reporter's details when the switch was never read back", async () => {
438
+ storedForm = buildForm();
439
+ setNull(storedForm, "isReporterDetailsRequired");
440
+
441
+ expect((await getPublicForm()).isReporterDetailsRequired).toBe(true);
442
+ });
443
+ });
444
+
445
+ describe("IncidentFormService.getPublicForm - custom field questions", () => {
446
+ test("lists only the fields the form asks, in order, required as the form says", async () => {
447
+ storedForm = buildForm({ customFieldSettings: FORM_QUESTIONS });
448
+
449
+ expect((await getPublicForm()).customFields).toEqual([
450
+ {
451
+ name: "Affected Location",
452
+ customFieldType: CustomFieldType.Text,
453
+ isRequired: false,
454
+ },
455
+ {
456
+ name: "Impact",
457
+ description: "How bad is it?",
458
+ customFieldType: CustomFieldType.Dropdown,
459
+ dropdownOptions: "Low\nHigh",
460
+ isRequired: true,
461
+ },
462
+ ]);
463
+ });
464
+
465
+ test("reads the project's fields for the form's own project, as root", async () => {
466
+ storedForm = buildForm({ customFieldSettings: FORM_QUESTIONS });
467
+
468
+ await getPublicForm();
469
+
470
+ expect(customFieldFindBy).toHaveBeenCalledTimes(1);
471
+
472
+ const findBy: {
473
+ query: Record<string, unknown>;
474
+ select: Record<string, unknown>;
475
+ props: Record<string, unknown>;
476
+ } = customFieldFindBy.mock.calls[0]![0] as never;
477
+
478
+ expect(findBy.query).toEqual({ projectId: PROJECT_ID });
479
+ expect(findBy.props).toEqual({ isRoot: true });
480
+ expect(findBy.select).toEqual({
481
+ name: true,
482
+ description: true,
483
+ customFieldType: true,
484
+ dropdownOptions: true,
485
+ variableKey: true,
486
+ sortOrder: true,
487
+ mapFromResourceType: true,
488
+ mapFromCustomFieldName: true,
489
+ });
490
+ });
491
+
492
+ /*
493
+ * The reporter must be asked the questions in the order the form's
494
+ * Questions card (and Declare Incident) lists them. Both read the fields
495
+ * by Sort Order, and DatabaseService appends _id to every sort, so fields
496
+ * without a sort order tie-break by id on both sides; with no sort at all
497
+ * the public page read them newest first instead (found by the live E2E
498
+ * run, IncidentForms.spec.ts G).
499
+ */
500
+ test("reads the fields in the dashboard's order: by Sort Order, the same sort the dashboard asks for", async () => {
501
+ storedForm = buildForm({ customFieldSettings: FORM_QUESTIONS });
502
+
503
+ await getPublicForm();
504
+
505
+ const findBy: { sort?: Record<string, unknown> } = customFieldFindBy.mock
506
+ .calls[0]![0] as never;
507
+
508
+ expect(findBy.sort).toEqual({ sortOrder: SortOrder.Ascending });
509
+
510
+ const dashboardSource: string = fs.readFileSync(
511
+ path.resolve(
512
+ __dirname,
513
+ "../../../../App/FeatureSet/Dashboard/src/Components/Incident/IncidentCustomFieldDefinitions.ts",
514
+ ),
515
+ "utf8",
516
+ );
517
+
518
+ expect(dashboardSource.replace(/\s+/g, "")).toContain(
519
+ "sort:{sortOrder:SortOrder.Ascending,}",
520
+ );
521
+ });
522
+
523
+ test.each([
524
+ ["no settings at all", null],
525
+ ["an empty object", {}],
526
+ [
527
+ "only Hidden and Default entries",
528
+ { internal_notes: "Hidden", cost_center: "Default" },
529
+ ],
530
+ ["only entries that are not settings", { impact: "Mandatory" }],
531
+ ])(
532
+ "asks nothing, and reads no fields, for %s",
533
+ async (_label: string, settings: Record<string, string> | null) => {
534
+ storedForm = buildForm();
535
+ (storedForm as unknown as Record<string, unknown>)[
536
+ "customFieldSettings"
537
+ ] = settings;
538
+
539
+ expect((await getPublicForm()).customFields).toEqual([]);
540
+ expect(customFieldFindBy).not.toHaveBeenCalled();
541
+ },
542
+ );
543
+
544
+ test("asks nothing about a field the settings name but the project no longer has", async () => {
545
+ storedForm = buildForm({
546
+ customFieldSettings: { deleted_field: "Required" },
547
+ });
548
+
549
+ expect((await getPublicForm()).customFields).toEqual([]);
550
+ });
551
+ });
552
+
553
+ /*
554
+ * A field mapped from a monitor field takes the monitor's value when the
555
+ * incident has a monitor - IncidentService applies the mapping last, over
556
+ * any answer - and a form's incident gets monitors from the form's
557
+ * template. So such a field is not asked when the template attaches
558
+ * monitors, as the dashboard's Declare Incident form does not ask it: a
559
+ * reporter must never be made to answer a question whose answer is thrown
560
+ * away.
561
+ */
562
+ describe("IncidentFormService.getPublicForm - fields copied from a monitor", () => {
563
+ const MONITOR_ID: string = "e1000000-0000-4000-8000-0000000000f1";
564
+
565
+ const REGION: IncidentCustomField = (() => {
566
+ const field: IncidentCustomField = customField({
567
+ name: "Region",
568
+ variableKey: "region",
569
+ customFieldType: CustomFieldType.Text,
570
+ sortOrder: 6,
571
+ });
572
+ field.mapFromResourceType = CustomFieldMappingSourceResource.Monitor;
573
+ field.mapFromCustomFieldName = "Region";
574
+ return field;
575
+ })();
576
+
577
+ function templateWithMonitors(count: number): IncidentTemplate {
578
+ const template: IncidentTemplate = new IncidentTemplate();
579
+ template._id = TEMPLATE_ID;
580
+ template.monitors = [];
581
+
582
+ for (let index: number = 0; index < count; index++) {
583
+ const monitor: Monitor = new Monitor();
584
+ monitor._id = MONITOR_ID;
585
+ template.monitors.push(monitor);
586
+ }
587
+
588
+ return template;
589
+ }
590
+
591
+ let templateFindOneBy: MockedFn;
592
+
593
+ beforeEach(() => {
594
+ customFieldFindBy.mockResolvedValue([...PROJECT_FIELDS, REGION] as never);
595
+ templateFindOneBy = jest
596
+ .spyOn(IncidentTemplateService, "findOneBy")
597
+ .mockResolvedValue(
598
+ templateWithMonitors(1) as never,
599
+ ) as unknown as MockedFn;
600
+ });
601
+
602
+ const ASKS_REGION: Record<string, string> = {
603
+ region: "Required",
604
+ impact: "Optional",
605
+ };
606
+
607
+ test("is not asked when the form's template attaches a monitor", async () => {
608
+ storedForm = buildForm({ customFieldSettings: ASKS_REGION });
609
+
610
+ expect(
611
+ (await getPublicForm()).customFields.map(
612
+ (field: { name: string }): string => {
613
+ return field.name;
614
+ },
615
+ ),
616
+ ).toEqual(["Impact"]);
617
+ });
618
+
619
+ test("reads only the template's monitors, from the form's own project, as root", async () => {
620
+ storedForm = buildForm({ customFieldSettings: ASKS_REGION });
621
+
622
+ await getPublicForm();
623
+
624
+ expect(templateFindOneBy).toHaveBeenCalledTimes(1);
625
+ expect(templateFindOneBy.mock.calls[0]![0]).toEqual({
626
+ query: { _id: TEMPLATE_ID, projectId: PROJECT_ID },
627
+ select: { monitors: { _id: true } },
628
+ props: { isRoot: true },
629
+ });
630
+ });
631
+
632
+ test("is asked, as the form says, when the template attaches no monitor", async () => {
633
+ storedForm = buildForm({ customFieldSettings: ASKS_REGION });
634
+ templateFindOneBy.mockResolvedValue(templateWithMonitors(0) as never);
635
+
636
+ expect((await getPublicForm()).customFields).toContainEqual({
637
+ name: "Region",
638
+ customFieldType: CustomFieldType.Text,
639
+ isRequired: true,
640
+ });
641
+ });
642
+
643
+ test("is asked when the form has no template, and no template is read", async () => {
644
+ storedForm = buildForm({ customFieldSettings: ASKS_REGION });
645
+ setNull(storedForm, "incidentTemplateId");
646
+
647
+ expect(
648
+ (await getPublicForm()).customFields.map(
649
+ (field: { name: string }): string => {
650
+ return field.name;
651
+ },
652
+ ),
653
+ ).toEqual(["Impact", "Region"]);
654
+ expect(templateFindOneBy).not.toHaveBeenCalled();
655
+ });
656
+
657
+ test("is asked when the template cannot be found in the form's project", async () => {
658
+ storedForm = buildForm({ customFieldSettings: ASKS_REGION });
659
+ templateFindOneBy.mockResolvedValue(null as never);
660
+
661
+ expect(
662
+ (await getPublicForm()).customFields.map(
663
+ (field: { name: string }): string => {
664
+ return field.name;
665
+ },
666
+ ),
667
+ ).toEqual(["Impact", "Region"]);
668
+ });
669
+
670
+ test("reads no template when the form asks no mapped field", async () => {
671
+ storedForm = buildForm({ customFieldSettings: FORM_QUESTIONS });
672
+
673
+ await getPublicForm();
674
+
675
+ expect(templateFindOneBy).not.toHaveBeenCalled();
676
+ });
677
+ });
678
+
679
+ describe("IncidentFormService.getPublicForm - severities", () => {
680
+ test("are not listed, nor read, when the form sets the severity itself", async () => {
681
+ const form: PublicIncidentForm = await getPublicForm();
682
+
683
+ expect(form).not.toHaveProperty("severities");
684
+ expect(form).not.toHaveProperty("defaultIncidentSeverityId");
685
+ expect(severityFindBy).not.toHaveBeenCalled();
686
+ });
687
+
688
+ test("are listed in the project's order, with only id, name and colour, when the reporter may choose", async () => {
689
+ storedForm = buildForm({ allowReporterToChooseSeverity: true });
690
+
691
+ const form: PublicIncidentForm = await getPublicForm();
692
+
693
+ expect(form.severities).toEqual([
694
+ { _id: CRITICAL_SEVERITY_ID, name: "Critical", color: "#ff0000" },
695
+ { _id: MINOR_SEVERITY_ID, name: "Minor", color: "#00ff00" },
696
+ { _id: UNCOLOURED_SEVERITY_ID, name: "Unsorted" },
697
+ ]);
698
+ expect(form.defaultIncidentSeverityId).toBe(CRITICAL_SEVERITY_ID);
699
+ });
700
+
701
+ test("are read from the form's own project, sorted by order, as root", async () => {
702
+ storedForm = buildForm({ allowReporterToChooseSeverity: true });
703
+
704
+ await getPublicForm();
705
+
706
+ const findBy: {
707
+ query: Record<string, unknown>;
708
+ select: Record<string, unknown>;
709
+ sort: Record<string, unknown>;
710
+ props: Record<string, unknown>;
711
+ } = severityFindBy.mock.calls[0]![0] as never;
712
+
713
+ expect(findBy.query).toEqual({ projectId: PROJECT_ID });
714
+ expect(findBy.sort).toEqual({ order: SortOrder.Ascending });
715
+ expect(findBy.props).toEqual({ isRoot: true });
716
+ expect(findBy.select).toEqual({
717
+ _id: true,
718
+ name: true,
719
+ color: true,
720
+ order: true,
721
+ });
722
+ });
723
+
724
+ test("preselect nothing when the form's own severity was deleted", async () => {
725
+ storedForm = buildForm({ allowReporterToChooseSeverity: true });
726
+ setNull(storedForm, "incidentSeverityId");
727
+
728
+ const form: PublicIncidentForm = await getPublicForm();
729
+
730
+ expect(form.severities).toHaveLength(3);
731
+ expect(form).not.toHaveProperty("defaultIncidentSeverityId");
732
+ });
733
+ });
734
+
735
+ describe("IncidentFormService.getPublicForm - captcha", () => {
736
+ test.each([[true], [false]])(
737
+ "asks for a captcha exactly when the instance has one on (%s)",
738
+ async (enabled: boolean) => {
739
+ captchaEnabled.mockReturnValue(enabled);
740
+
741
+ expect((await getPublicForm()).isCaptchaRequired).toBe(enabled);
742
+ },
743
+ );
744
+ });
745
+
746
+ describe("IncidentFormService.getPublicForm - forms a visitor may not use", () => {
747
+ test.each([
748
+ ["an empty key", ""],
749
+ ["no key at all", undefined],
750
+ ["a word", "report-a-problem"],
751
+ ["a form id's worth of junk", "a1b2c3d4-0000-4000-8000-0000000000fZ"],
752
+ ["a key with a character too many", `${SHARE_KEY}0`],
753
+ ["an overlong key", "a".repeat(5000)],
754
+ ["a SQL fragment", "' OR '1'='1"],
755
+ ])(
756
+ "refuses %s as not available, without asking Postgres",
757
+ async (_label: string, shareKey: string | undefined) => {
758
+ const error: Exception | undefined = await refusal(
759
+ getPublicForm({ shareKey }),
760
+ );
761
+
762
+ expect(error).toBeInstanceOf(NotFoundException);
763
+ expect(error?.message).toBe(INCIDENT_FORM_NOT_AVAILABLE_MESSAGE);
764
+ expect(formFindOneBy).not.toHaveBeenCalled();
765
+ },
766
+ );
767
+
768
+ test("refuses a key no form holds as not available", async () => {
769
+ const error: Exception | undefined = await refusal(
770
+ getPublicForm({ shareKey: OTHER_SHARE_KEY }),
771
+ );
772
+
773
+ expect(error).toBeInstanceOf(NotFoundException);
774
+ expect(error?.code).toBe(ExceptionCode.NotFoundException);
775
+ expect(error?.message).toBe(INCIDENT_FORM_NOT_AVAILABLE_MESSAGE);
776
+ });
777
+
778
+ test("refuses a form that is turned off as not available", async () => {
779
+ storedForm = buildForm({ isEnabled: false });
780
+
781
+ const error: Exception | undefined = await refusal(getPublicForm());
782
+
783
+ expect(error).toBeInstanceOf(NotFoundException);
784
+ expect(error?.message).toBe(INCIDENT_FORM_NOT_AVAILABLE_MESSAGE);
785
+ });
786
+
787
+ test("treats a switch that was never read back as off", async () => {
788
+ storedForm = buildForm();
789
+ setNull(storedForm, "isEnabled");
790
+
791
+ expect(await refusal(getPublicForm())).toBeInstanceOf(NotFoundException);
792
+ });
793
+
794
+ test("refuses a form whose project is off plan as not available", async () => {
795
+ jest
796
+ .spyOn(IncidentFormService, "isProjectOnPlan")
797
+ .mockResolvedValue(false as never);
798
+
799
+ const error: Exception | undefined = await refusal(getPublicForm());
800
+
801
+ expect(error).toBeInstanceOf(NotFoundException);
802
+ expect(error?.message).toBe(INCIDENT_FORM_NOT_AVAILABLE_MESSAGE);
803
+ });
804
+
805
+ test("checks the plan of the form's own project", async () => {
806
+ const onPlan: MockedFn = jest
807
+ .spyOn(IncidentFormService, "isProjectOnPlan")
808
+ .mockResolvedValue(true as never) as unknown as MockedFn;
809
+
810
+ await getPublicForm();
811
+
812
+ expect(onPlan).toHaveBeenCalledTimes(1);
813
+ expect(String(onPlan.mock.calls[0]![0])).toBe(PROJECT_ID.toString());
814
+ });
815
+
816
+ /*
817
+ * The point of one message: a prober trying links cannot tell a link that
818
+ * never existed from a form that is off or a project that stopped paying.
819
+ */
820
+ test("gives an unknown link, a junk link, a form turned off and a project off plan the very same answer", async () => {
821
+ const unknown: Exception | undefined = await refusal(
822
+ getPublicForm({ shareKey: OTHER_SHARE_KEY }),
823
+ );
824
+ const junk: Exception | undefined = await refusal(
825
+ getPublicForm({ shareKey: "junk" }),
826
+ );
827
+
828
+ storedForm = buildForm({ isEnabled: false });
829
+ const off: Exception | undefined = await refusal(getPublicForm());
830
+
831
+ storedForm = buildForm();
832
+ jest
833
+ .spyOn(IncidentFormService, "isProjectOnPlan")
834
+ .mockResolvedValue(false as never);
835
+ const offPlan: Exception | undefined = await refusal(getPublicForm());
836
+
837
+ const answers: Array<[string | undefined, unknown, unknown]> = [
838
+ unknown,
839
+ junk,
840
+ off,
841
+ offPlan,
842
+ ].map((error: Exception | undefined) => {
843
+ return [error?.constructor.name, error?.code, error?.message];
844
+ });
845
+
846
+ expect(
847
+ new Set(
848
+ answers.map((answer: unknown) => {
849
+ return JSON.stringify(answer);
850
+ }),
851
+ ).size,
852
+ ).toBe(1);
853
+ expect(answers[0]).toEqual([
854
+ "NotFoundException",
855
+ 404,
856
+ INCIDENT_FORM_NOT_AVAILABLE_MESSAGE,
857
+ ]);
858
+ });
859
+
860
+ test("reads nothing else about a form it refuses", async () => {
861
+ storedForm = buildForm({
862
+ isEnabled: false,
863
+ allowReporterToChooseSeverity: true,
864
+ customFieldSettings: FORM_QUESTIONS,
865
+ });
866
+
867
+ await refusal(getPublicForm());
868
+
869
+ expect(customFieldFindBy).not.toHaveBeenCalled();
870
+ expect(severityFindBy).not.toHaveBeenCalled();
871
+ });
872
+ });
873
+
874
+ describe("IncidentFormService.getPublicForm - the IP allowlist", () => {
875
+ test("lets anyone in when the form has no allowlist", async () => {
876
+ storedForm = buildForm({ ipWhitelist: "" });
877
+
878
+ await expect(
879
+ getPublicForm({ clientIp: "192.0.2.99" }),
880
+ ).resolves.toBeDefined();
881
+ });
882
+
883
+ test("lets a listed address in", async () => {
884
+ storedForm = buildForm({ ipWhitelist: `198.51.100.1\n${CLIENT_IP}` });
885
+
886
+ await expect(getPublicForm()).resolves.toBeDefined();
887
+ });
888
+
889
+ test("lets an address inside a listed range in", async () => {
890
+ storedForm = buildForm({ ipWhitelist: "203.0.113.0/24" });
891
+
892
+ await expect(getPublicForm()).resolves.toBeDefined();
893
+ });
894
+
895
+ test("refuses any other address with a 403 that says what is wrong", async () => {
896
+ storedForm = buildForm({ ipWhitelist: "198.51.100.0/24" });
897
+
898
+ const error: Exception | undefined = await refusal(getPublicForm());
899
+
900
+ expect(error).toBeInstanceOf(ForbiddenException);
901
+ expect(error?.code).toBe(403);
902
+ expect(error?.message).toBe(INCIDENT_FORM_NETWORK_NOT_ALLOWED_MESSAGE);
903
+ });
904
+
905
+ test("refuses a request whose address cannot be established, and logs it", async () => {
906
+ storedForm = buildForm({ ipWhitelist: CLIENT_IP });
907
+
908
+ const error: Exception | undefined = await refusal(
909
+ getPublicForm({ clientIp: undefined }),
910
+ );
911
+
912
+ expect(error).toBeInstanceOf(ForbiddenException);
913
+ expect(logger.error).toHaveBeenCalled();
914
+ });
915
+
916
+ test("does not need an address when there is no allowlist", async () => {
917
+ await expect(getPublicForm({ clientIp: undefined })).resolves.toBeDefined();
918
+ });
919
+
920
+ test("checks the network only for a form that is on and on plan", async () => {
921
+ storedForm = buildForm({
922
+ isEnabled: false,
923
+ ipWhitelist: "198.51.100.0/24",
924
+ });
925
+
926
+ expect(await refusal(getPublicForm())).toBeInstanceOf(NotFoundException);
927
+ });
928
+
929
+ test("reads nothing else about the form for a visitor it refuses", async () => {
930
+ storedForm = buildForm({
931
+ ipWhitelist: "198.51.100.0/24",
932
+ allowReporterToChooseSeverity: true,
933
+ customFieldSettings: FORM_QUESTIONS,
934
+ });
935
+
936
+ await refusal(getPublicForm());
937
+
938
+ expect(customFieldFindBy).not.toHaveBeenCalled();
939
+ expect(severityFindBy).not.toHaveBeenCalled();
940
+ });
941
+ });
942
+
943
+ describe("IncidentFormService.isClientIpAllowed", () => {
944
+ test.each([
945
+ ["no list", undefined, CLIENT_IP, true],
946
+ ["a null list", null, CLIENT_IP, true],
947
+ ["an empty list", "", CLIENT_IP, true],
948
+ [
949
+ "a list of blank lines, as a cleared textarea leaves",
950
+ "\n \r\n\t\n",
951
+ CLIENT_IP,
952
+ true,
953
+ ],
954
+ ["an exact address", CLIENT_IP, CLIENT_IP, true],
955
+ ["a range holding the address", "203.0.113.0/24", CLIENT_IP, true],
956
+ [
957
+ "Windows line endings",
958
+ `198.51.100.1\r\n${CLIENT_IP}\r\n`,
959
+ CLIENT_IP,
960
+ true,
961
+ ],
962
+ ["padding around an entry", ` ${CLIENT_IP} `, CLIENT_IP, true],
963
+ ["an IPv6 address listed exactly", "2001:db8::1", "2001:db8::1", true],
964
+ ["an IPv6 address listed in capitals", "2001:DB8::1", "2001:db8::1", true],
965
+ [
966
+ "an IPv6 address listed written out in full",
967
+ "2001:0db8:0000:0000:0000:0000:0000:0001",
968
+ "2001:db8::1",
969
+ true,
970
+ ],
971
+ [
972
+ "a client address reported in another spelling",
973
+ "2001:db8::1",
974
+ "2001:DB8:0:0:0:0:0:1",
975
+ true,
976
+ ],
977
+ ["another IPv6 address", "2001:db8::2", "2001:db8::1", false],
978
+ /*
979
+ * An IPv4 visitor that reaches the check in an IPv6 spelling - a proxy
980
+ * that writes the mapped address in hex, say - is the IPv4 address, for
981
+ * an entry and for a range.
982
+ */
983
+ [
984
+ "a client in the IPv4-mapped spelling",
985
+ CLIENT_IP,
986
+ `::ffff:${CLIENT_IP}`,
987
+ true,
988
+ ],
989
+ [
990
+ "a client in the IPv4-mapped spelling, in hex",
991
+ CLIENT_IP,
992
+ "::ffff:cb00:7107",
993
+ true,
994
+ ],
995
+ [
996
+ "a range holding a client in the IPv4-mapped spelling, in hex",
997
+ "203.0.113.0/24",
998
+ "::FFFF:CB00:7107",
999
+ true,
1000
+ ],
1001
+ [
1002
+ "another address's IPv4-mapped spelling",
1003
+ CLIENT_IP,
1004
+ "::ffff:198.51.100.1",
1005
+ false,
1006
+ ],
1007
+ // Not an IPv4 address in disguise: "::ffff:0:" is another prefix.
1008
+ [
1009
+ "an IPv4-translated address, which is not the IPv4 one",
1010
+ CLIENT_IP,
1011
+ `::ffff:0:${CLIENT_IP}`,
1012
+ false,
1013
+ ],
1014
+ ["another address", "198.51.100.1", CLIENT_IP, false],
1015
+ ["a range not holding the address", "198.51.100.0/24", CLIENT_IP, false],
1016
+ ["no address, with a list", CLIENT_IP, undefined, false],
1017
+ ["an empty address, with a list", CLIENT_IP, "", false],
1018
+ ["an address that is not one", CLIENT_IP, "not-an-address", false],
1019
+ ["only a malformed entry", "not-a-network", CLIENT_IP, false],
1020
+ ["a malformed range", "203.0.113.0/99", CLIENT_IP, false],
1021
+ ])(
1022
+ "with %s it answers correctly",
1023
+ (
1024
+ _label: string,
1025
+ ipWhitelist: string | null | undefined,
1026
+ clientIp: string | undefined,
1027
+ allowed: boolean,
1028
+ ) => {
1029
+ expect(
1030
+ IncidentFormService.isClientIpAllowed({ ipWhitelist, clientIp }),
1031
+ ).toBe(allowed);
1032
+ },
1033
+ );
1034
+
1035
+ /*
1036
+ * The one address to check is resolved before this is asked (from the
1037
+ * trusted end of X-Forwarded-For). A forwarded-for chain handed in whole
1038
+ * is not an address, so it is refused rather than matched entry by entry.
1039
+ */
1040
+ test("refuses a whole forwarded-for chain rather than matching any entry in it", () => {
1041
+ expect(
1042
+ IncidentFormService.isClientIpAllowed({
1043
+ ipWhitelist: CLIENT_IP,
1044
+ clientIp: `${CLIENT_IP}, 192.0.2.1`,
1045
+ }),
1046
+ ).toBe(false);
1047
+ });
1048
+
1049
+ /*
1050
+ * An IPv4 visitor of a dual-stack listener, as the routes resolve it:
1051
+ * Node reports the socket's peer as "::ffff:203.0.113.7", and an nginx
1052
+ * listening dual-stack writes it so into X-Forwarded-For. It reaches the
1053
+ * check as the IPv4 address, which the list's IPv4 entry or range lets in
1054
+ * - the list itself refuses that spelling (validateIncidentFormIpAllowlist).
1055
+ */
1056
+ test.each([
1057
+ [
1058
+ "the socket's peer, with no proxy in front",
1059
+ {
1060
+ headers: {},
1061
+ socket: { remoteAddress: `::ffff:${CLIENT_IP}` },
1062
+ },
1063
+ 0,
1064
+ ],
1065
+ [
1066
+ "the address the proxy appended",
1067
+ {
1068
+ headers: { "x-forwarded-for": `::ffff:${CLIENT_IP}` },
1069
+ socket: { remoteAddress: "127.0.0.1" },
1070
+ },
1071
+ 1,
1072
+ ],
1073
+ ])(
1074
+ "lets in an IPv4 visitor reported in the IPv4-mapped spelling, as %s",
1075
+ (
1076
+ _label: string,
1077
+ request: ClientIpRequestLike,
1078
+ trustedProxyHops: number,
1079
+ ) => {
1080
+ const clientIp: string | undefined = resolveClientIp(request, {
1081
+ trustedProxyHops: trustedProxyHops,
1082
+ });
1083
+
1084
+ expect(clientIp).toBe(CLIENT_IP);
1085
+
1086
+ for (const ipWhitelist of [CLIENT_IP, "203.0.113.0/24"]) {
1087
+ expect(
1088
+ IncidentFormService.isClientIpAllowed({ ipWhitelist, clientIp }),
1089
+ ).toBe(true);
1090
+ }
1091
+ },
1092
+ );
1093
+ });
1094
+
1095
+ describe("IncidentFormService.isProjectOnPlan", () => {
1096
+ const projectId: ObjectID = PROJECT_ID;
1097
+ let getCurrentPlan: MockedFn;
1098
+ let planCheck: MockedFn;
1099
+
1100
+ beforeEach(() => {
1101
+ getCurrentPlan = jest
1102
+ .spyOn(ProjectService, "getCurrentPlan")
1103
+ .mockResolvedValue({
1104
+ plan: PlanType.Growth,
1105
+ isSubscriptionUnpaid: false,
1106
+ } as never) as unknown as MockedFn;
1107
+ planCheck = jest
1108
+ .spyOn(SubscriptionPlan, "isFeatureAccessibleOnCurrentPlan")
1109
+ .mockReturnValue(true) as unknown as MockedFn;
1110
+ });
1111
+
1112
+ test("with billing off every project is on plan, and no plan is read", async () => {
1113
+ setBillingEnabled(false);
1114
+
1115
+ expect(await IncidentFormService.isProjectOnPlan(projectId)).toBe(true);
1116
+ expect(getCurrentPlan).not.toHaveBeenCalled();
1117
+ });
1118
+
1119
+ test("with billing on it asks whether the project's plan includes Growth features", async () => {
1120
+ setBillingEnabled(true);
1121
+ getCurrentPlan.mockResolvedValue({
1122
+ plan: PlanType.Scale,
1123
+ isSubscriptionUnpaid: false,
1124
+ });
1125
+
1126
+ expect(await IncidentFormService.isProjectOnPlan(projectId)).toBe(true);
1127
+ expect(getCurrentPlan).toHaveBeenCalledWith(projectId);
1128
+ expect(planCheck).toHaveBeenCalledTimes(1);
1129
+ expect(planCheck.mock.calls[0]![0]).toBe(PlanType.Growth);
1130
+ expect(planCheck.mock.calls[0]![1]).toBe(PlanType.Scale);
1131
+ });
1132
+
1133
+ test("a plan below Growth is off plan", async () => {
1134
+ setBillingEnabled(true);
1135
+ getCurrentPlan.mockResolvedValue({
1136
+ plan: PlanType.Free,
1137
+ isSubscriptionUnpaid: false,
1138
+ });
1139
+ planCheck.mockReturnValue(false);
1140
+
1141
+ expect(await IncidentFormService.isProjectOnPlan(projectId)).toBe(false);
1142
+ });
1143
+
1144
+ test("no plan at all is off plan (fails closed)", async () => {
1145
+ setBillingEnabled(true);
1146
+ getCurrentPlan.mockResolvedValue({
1147
+ plan: null,
1148
+ isSubscriptionUnpaid: false,
1149
+ });
1150
+
1151
+ expect(await IncidentFormService.isProjectOnPlan(projectId)).toBe(false);
1152
+ expect(planCheck).not.toHaveBeenCalled();
1153
+ });
1154
+
1155
+ test("a plan that cannot be read is off plan, logged, never thrown", async () => {
1156
+ setBillingEnabled(true);
1157
+ getCurrentPlan.mockRejectedValue(new Error("Project ID is invalid"));
1158
+
1159
+ expect(await IncidentFormService.isProjectOnPlan(projectId)).toBe(false);
1160
+ expect(logger.warn).toHaveBeenCalled();
1161
+ });
1162
+
1163
+ test("with billing on, a form of an off-plan project is not available", async () => {
1164
+ setBillingEnabled(true);
1165
+ planCheck.mockReturnValue(false);
1166
+
1167
+ const error: Exception | undefined = await refusal(getPublicForm());
1168
+
1169
+ expect(error).toBeInstanceOf(NotFoundException);
1170
+ expect(error?.message).toBe(INCIDENT_FORM_NOT_AVAILABLE_MESSAGE);
1171
+ expect(getCurrentPlan).toHaveBeenCalledWith(PROJECT_ID);
1172
+ });
1173
+
1174
+ test("with billing on, a form of a project on plan is served", async () => {
1175
+ setBillingEnabled(true);
1176
+
1177
+ await expect(getPublicForm()).resolves.toBeDefined();
1178
+ });
1179
+
1180
+ /*
1181
+ * The check stands in for the whole of the model's billing gate, which
1182
+ * refuses every dashboard request of a project whose subscription is
1183
+ * unpaid - its admins cannot even turn the form off - so the link must
1184
+ * not keep declaring incidents meanwhile, whatever the plan.
1185
+ */
1186
+ test.each([[PlanType.Growth], [PlanType.Scale], [PlanType.Enterprise]])(
1187
+ "an unpaid subscription on %s is off plan",
1188
+ async (plan: PlanType) => {
1189
+ setBillingEnabled(true);
1190
+ getCurrentPlan.mockResolvedValue({
1191
+ plan: plan,
1192
+ isSubscriptionUnpaid: true,
1193
+ });
1194
+
1195
+ expect(await IncidentFormService.isProjectOnPlan(projectId)).toBe(false);
1196
+ expect(planCheck).not.toHaveBeenCalled();
1197
+ },
1198
+ );
1199
+
1200
+ test("with billing on, a form of an unpaid project is not available, exactly as an unknown link", async () => {
1201
+ setBillingEnabled(true);
1202
+ getCurrentPlan.mockResolvedValue({
1203
+ plan: PlanType.Growth,
1204
+ isSubscriptionUnpaid: true,
1205
+ });
1206
+
1207
+ const unpaid: Exception | undefined = await refusal(getPublicForm());
1208
+ const unknown: Exception | undefined = await refusal(
1209
+ getPublicForm({ shareKey: OTHER_SHARE_KEY }),
1210
+ );
1211
+
1212
+ expect(unpaid).toBeInstanceOf(NotFoundException);
1213
+ expect(unpaid?.message).toBe(INCIDENT_FORM_NOT_AVAILABLE_MESSAGE);
1214
+ expect([unpaid?.code, unpaid?.message]).toEqual([
1215
+ unknown?.code,
1216
+ unknown?.message,
1217
+ ]);
1218
+ expect(customFieldFindBy).not.toHaveBeenCalled();
1219
+ });
1220
+
1221
+ test("with billing on, a form of an unpaid project declares nothing", async () => {
1222
+ setBillingEnabled(true);
1223
+ getCurrentPlan.mockResolvedValue({
1224
+ plan: PlanType.Growth,
1225
+ isSubscriptionUnpaid: true,
1226
+ });
1227
+
1228
+ const incidentCreate: MockedFn = jest
1229
+ .spyOn(IncidentService, "create")
1230
+ .mockResolvedValue(undefined as never) as unknown as MockedFn;
1231
+
1232
+ const error: Exception | undefined = await refusal(
1233
+ IncidentFormService.submitPublicForm({
1234
+ shareKey: SHARE_KEY,
1235
+ request: {
1236
+ data: {
1237
+ title: "Checkout is down",
1238
+ reporterName: "Jane Doe",
1239
+ reporterEmail: "jane@example.com",
1240
+ },
1241
+ },
1242
+ clientIp: CLIENT_IP,
1243
+ }),
1244
+ );
1245
+
1246
+ expect(error).toBeInstanceOf(NotFoundException);
1247
+ expect(error?.message).toBe(INCIDENT_FORM_NOT_AVAILABLE_MESSAGE);
1248
+ expect(incidentCreate).not.toHaveBeenCalled();
1249
+ });
1250
+
1251
+ test("a paid subscription on a plan with forms stays on plan", async () => {
1252
+ setBillingEnabled(true);
1253
+ getCurrentPlan.mockResolvedValue({
1254
+ plan: PlanType.Growth,
1255
+ isSubscriptionUnpaid: false,
1256
+ });
1257
+
1258
+ expect(await IncidentFormService.isProjectOnPlan(projectId)).toBe(true);
1259
+ });
1260
+ });