@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.
- package/Models/DatabaseModels/AIConversation.ts +127 -1
- package/Models/DatabaseModels/CephCluster.ts +12 -0
- package/Models/DatabaseModels/CloudResource.ts +12 -0
- package/Models/DatabaseModels/DatabaseServer.ts +12 -0
- package/Models/DatabaseModels/DockerHost.ts +12 -0
- package/Models/DatabaseModels/DockerSwarmCluster.ts +12 -0
- package/Models/DatabaseModels/GlobalOidc.ts +0 -4
- package/Models/DatabaseModels/GlobalOidcProject.ts +0 -4
- package/Models/DatabaseModels/GlobalSso.ts +0 -4
- package/Models/DatabaseModels/GlobalSsoProject.ts +0 -4
- package/Models/DatabaseModels/Host.ts +12 -0
- package/Models/DatabaseModels/IncidentForm.ts +594 -0
- package/Models/DatabaseModels/IncidentFormSubmission.ts +283 -0
- package/Models/DatabaseModels/IncidentTemplate.ts +59 -0
- package/Models/DatabaseModels/Index.ts +14 -0
- package/Models/DatabaseModels/IoTFleet.ts +12 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +12 -0
- package/Models/DatabaseModels/MessageQueue.ts +1102 -0
- package/Models/DatabaseModels/MessageQueueLabelRule.ts +553 -0
- package/Models/DatabaseModels/MessageQueueOwnerRule.ts +633 -0
- package/Models/DatabaseModels/MessageQueueOwnerTeam.ts +491 -0
- package/Models/DatabaseModels/MessageQueueOwnerUser.ts +490 -0
- package/Models/DatabaseModels/PodmanHost.ts +12 -0
- package/Models/DatabaseModels/Project.ts +59 -0
- package/Models/DatabaseModels/ProjectOidc.ts +0 -4
- package/Models/DatabaseModels/ProjectSso.ts +0 -4
- package/Models/DatabaseModels/ProxmoxCluster.ts +12 -0
- package/Models/DatabaseModels/RumApplication.ts +12 -0
- package/Models/DatabaseModels/ServerlessFunction.ts +12 -0
- package/Models/DatabaseModels/Service.ts +12 -0
- package/Models/DatabaseModels/StatusPageOidc.ts +0 -4
- package/Models/DatabaseModels/StatusPageSso.ts +0 -4
- package/Models/DatabaseModels/TeamComplianceSetting.ts +47 -2
- package/Models/DatabaseModels/VMwareVCenter.ts +12 -0
- package/Server/API/AIChatAPI.ts +13 -114
- package/Server/API/AIInvestigationAPI.ts +3 -3
- package/Server/API/AIInvestigationConversationAPI.ts +266 -0
- package/Server/API/IncidentFormAPI.ts +376 -0
- package/Server/API/ProjectOIDC.ts +16 -25
- package/Server/API/ProjectSSO.ts +16 -24
- package/Server/API/StatusPageAPI.ts +42 -77
- package/Server/Enterprise/EnterpriseEdition.ts +93 -28
- package/Server/Enterprise/EnterpriseFeature.ts +25 -3
- package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +1 -1
- package/Server/Enterprise/EnterpriseServerModule.ts +4 -1
- package/Server/EnvironmentConfig.ts +1 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.ts +129 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.ts +52 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.ts +75 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.ts +477 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Middleware/IncidentFormRateLimit.ts +847 -0
- package/Server/Middleware/UserAuthorization.ts +16 -38
- package/Server/Services/AIService.ts +10 -13
- package/Server/Services/IncidentCustomFieldService.ts +112 -2
- package/Server/Services/IncidentEpisodeMemberService.ts +24 -4
- package/Server/Services/IncidentEpisodeService.ts +10 -1
- package/Server/Services/IncidentFormService.ts +1566 -0
- package/Server/Services/IncidentFormSubmissionService.ts +97 -0
- package/Server/Services/IncidentGroupingEngineService.ts +45 -8
- package/Server/Services/IncidentService.ts +123 -5
- package/Server/Services/IncidentTemplateService.ts +22 -0
- package/Server/Services/Index.ts +14 -0
- package/Server/Services/MessageQueueLabelRuleEngineService.ts +321 -0
- package/Server/Services/MessageQueueLabelRuleService.ts +14 -0
- package/Server/Services/MessageQueueOwnerRuleEngineService.ts +412 -0
- package/Server/Services/MessageQueueOwnerRuleService.ts +14 -0
- package/Server/Services/MessageQueueOwnerTeamService.ts +62 -0
- package/Server/Services/MessageQueueOwnerUserService.ts +62 -0
- package/Server/Services/MessageQueueService.ts +1463 -0
- package/Server/Services/OpenTelemetryIngestService.ts +37 -4
- package/Server/Services/ProjectService.ts +0 -59
- package/Server/Services/StatusPagePrivateUserService.ts +1 -4
- package/Server/Services/StatusPageService.ts +1 -51
- package/Server/Services/TeamComplianceSettingService.ts +163 -42
- package/Server/Services/UserNotificationRuleService.ts +8 -1
- package/Server/Services/WorkspaceNotificationSummaryService.ts +12 -3
- package/Server/Types/Database/Permissions/CreatePermission.ts +17 -3
- package/Server/Types/Database/Permissions/EditionPermission.ts +140 -38
- package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +16 -0
- package/Server/Types/Database/Permissions/TablePermission.ts +3 -3
- package/Server/Types/Database/Permissions/UpdatePermission.ts +20 -6
- package/Server/Utils/AI/Chat/AgentContextCompactor.ts +200 -0
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +307 -48
- package/Server/Utils/AI/Chat/ChatRunCancellation.ts +156 -0
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +78 -18
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
- package/Server/Utils/AI/Chat/ToolOutputPager.ts +322 -0
- package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +88 -30
- package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +13 -1
- package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +0 -32
- package/Server/Utils/AI/CodeFix/FixRunBudget.ts +27 -25
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +7 -1
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +34 -14
- package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +69 -24
- package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +9 -0
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +53 -10
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +27 -10
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +27 -10
- package/Server/Utils/AI/SRE/InvestigationQueue.ts +16 -19
- package/Server/Utils/AI/SRE/InvestigationThread.ts +646 -0
- package/Server/Utils/AI/SRE/InvestigationThreadService.ts +715 -0
- package/Server/Utils/AI/Toolbox/Serializer.ts +10 -3
- package/Server/Utils/EditionEnforcement.ts +19 -202
- package/Server/Utils/LLM/LLMService.ts +259 -63
- package/Server/Utils/RelaxedScimTeamLocksReport.ts +245 -0
- package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
- package/Server/Utils/SameOriginRequest.ts +203 -0
- package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +4 -1
- package/Server/Utils/Telemetry/MessageQueueDiscovery.ts +1402 -0
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +4 -1
- package/Server/Utils/VendorAssets.ts +3 -3
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +3 -1
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +3 -1
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +10 -1
- package/Tests/App/Accounts/IncidentFormPage.test.tsx +2036 -0
- package/Tests/App/Accounts/IncidentFormServerMessages.test.ts +119 -0
- package/Tests/App/Accounts/IncidentFormUtils.test.ts +1157 -0
- package/Tests/App/Accounts/LoginPasskey.test.tsx +6 -4
- package/Tests/App/Accounts/LoginSso.test.tsx +724 -0
- package/Tests/App/AdminDashboard/AdminAuthenticationRequireSso.test.tsx +338 -0
- package/Tests/App/AdminDashboard/AdminAuthenticationSsoCardLocales.test.tsx +331 -0
- package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +47 -33
- package/Tests/App/AdminDashboard/GlobalSsoOidcPages.test.tsx +1021 -0
- package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +876 -78
- package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +20 -3
- package/Tests/App/Dashboard/CephProxmoxVMwareDocumentationCards.test.tsx +359 -0
- package/Tests/App/Dashboard/CephSetupGuide.test.ts +961 -0
- package/Tests/App/Dashboard/CloudDocumentationCard.test.tsx +381 -95
- package/Tests/App/Dashboard/CloudSetupGuide.test.ts +1463 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +174 -12
- package/Tests/App/Dashboard/ContainerSetupGuideCards.test.tsx +345 -0
- package/Tests/App/Dashboard/CoreListEmptyStates.test.tsx +501 -0
- package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +0 -1
- package/Tests/App/Dashboard/DatabaseDocumentationCard.test.tsx +400 -0
- package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +5 -0
- package/Tests/App/Dashboard/DatabaseSetupGuide.test.ts +1869 -0
- package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +44 -0
- package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +50 -9
- package/Tests/App/Dashboard/DockerSetupGuide.test.ts +899 -0
- package/Tests/App/Dashboard/DockerSwarmSetupGuide.test.ts +964 -0
- package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +141 -7
- package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +52 -39
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +7 -1
- package/Tests/App/Dashboard/GettingStartedTaskRoutes.test.tsx +331 -0
- package/Tests/App/Dashboard/HomeOverviewStatsFirstRun.test.tsx +925 -0
- package/Tests/App/Dashboard/HostDocumentationCard.test.tsx +225 -0
- package/Tests/App/Dashboard/HostSetupGuide.test.ts +1183 -0
- package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +69 -45
- package/Tests/App/Dashboard/IncidentCreateTemplateCustomFieldSettings.test.tsx +745 -0
- package/Tests/App/Dashboard/IncidentCreateTemplateOwners.test.tsx +414 -0
- package/Tests/App/Dashboard/IncidentCustomFieldCreateSettingsForm.test.ts +1042 -0
- package/Tests/App/Dashboard/IncidentCustomFieldSettingsCard.test.tsx +1816 -0
- package/Tests/App/Dashboard/IncidentFormPlan.test.ts +89 -0
- package/Tests/App/Dashboard/IncidentFormShareLinkCard.test.tsx +846 -0
- package/Tests/App/Dashboard/IncidentFormsPages.test.tsx +1324 -0
- package/Tests/App/Dashboard/IncidentTemplateCustomFieldSettings.test.tsx +630 -0
- package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +13 -0
- package/Tests/App/Dashboard/IncomingEmailMonitorLinkVerifyHint.test.tsx +104 -0
- package/Tests/App/Dashboard/InvestigationConversation.test.tsx +1107 -0
- package/Tests/App/Dashboard/InvestigationConversationData.test.ts +546 -0
- package/Tests/App/Dashboard/InvestigationEvidenceList.test.tsx +170 -5
- package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +207 -2
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +729 -34
- package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +9 -21
- package/Tests/App/Dashboard/InvestigationReportView.test.tsx +274 -37
- package/Tests/App/Dashboard/InvestigationStatusBadge.test.tsx +130 -0
- package/Tests/App/Dashboard/IoTDocumentationCard.test.tsx +218 -0
- package/Tests/App/Dashboard/IoTSetupGuide.test.ts +1124 -0
- package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +20 -6
- package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +107 -83
- package/Tests/App/Dashboard/KubernetesSetupGuide.test.ts +787 -0
- package/Tests/App/Dashboard/MessageQueueBreadcrumbs.test.tsx +228 -0
- package/Tests/App/Dashboard/MessageQueueBrokerHealth.test.tsx +1538 -0
- package/Tests/App/Dashboard/MessageQueueBrokerHealthMetricChart.test.tsx +969 -0
- package/Tests/App/Dashboard/MessageQueueBrokerHealthReads.test.ts +889 -0
- package/Tests/App/Dashboard/MessageQueueBrokerMetricsReach.test.ts +236 -0
- package/Tests/App/Dashboard/MessageQueueDocumentationCard.test.tsx +479 -0
- package/Tests/App/Dashboard/MessageQueueListPages.test.tsx +1232 -0
- package/Tests/App/Dashboard/MessageQueueMetricDescriptions.test.ts +244 -0
- package/Tests/App/Dashboard/MessageQueueMonitorLink.test.ts +952 -0
- package/Tests/App/Dashboard/MessageQueueMonitorLinkMonitorCreate.test.tsx +859 -0
- package/Tests/App/Dashboard/MessageQueueOverview.test.tsx +1773 -0
- package/Tests/App/Dashboard/MessageQueueOverviewPresentation.test.ts +196 -0
- package/Tests/App/Dashboard/MessageQueuePresentation.test.ts +799 -0
- package/Tests/App/Dashboard/MessageQueueProducersConsumers.test.tsx +680 -0
- package/Tests/App/Dashboard/MessageQueueRoutes.test.tsx +383 -0
- package/Tests/App/Dashboard/MessageQueueRulePages.test.tsx +278 -0
- package/Tests/App/Dashboard/MessageQueueSetupGuide.test.ts +2778 -0
- package/Tests/App/Dashboard/MessageQueueSideMenus.test.tsx +273 -0
- package/Tests/App/Dashboard/MessageQueueSpanPopulations.test.ts +930 -0
- package/Tests/App/Dashboard/MessageQueueSpanStore.ts +351 -0
- package/Tests/App/Dashboard/MessageQueueTelemetryScope.test.ts +375 -0
- package/Tests/App/Dashboard/MessageQueueTelemetryScopeTabs.test.tsx +668 -0
- package/Tests/App/Dashboard/MessageQueueViewPages.test.tsx +685 -0
- package/Tests/App/Dashboard/MonitorAndHomeNotOperationalMenus.test.tsx +342 -0
- package/Tests/App/Dashboard/MonitorCreateFirstRunDefaults.test.tsx +623 -0
- package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +442 -0
- package/Tests/App/Dashboard/MonitorLogsPageViewSummary.test.tsx +814 -0
- package/Tests/App/Dashboard/MonitorSetupCardBodies.test.tsx +246 -0
- package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +8 -0
- package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +27 -12
- package/Tests/App/Dashboard/PlanGatedPage.test.tsx +292 -0
- package/Tests/App/Dashboard/PodmanSetupGuide.test.ts +948 -0
- package/Tests/App/Dashboard/ProductOrientation.test.tsx +5 -4
- package/Tests/App/Dashboard/ProductSettingsSectionCollapsed.test.tsx +266 -0
- package/Tests/App/Dashboard/ProxmoxSetupGuide.test.ts +1318 -0
- package/Tests/App/Dashboard/ResourceAiAgentAttentionServerParity.test.ts +555 -0
- package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +298 -9
- package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +453 -0
- package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +16 -0
- package/Tests/App/Dashboard/ResourceConnectionGuideCard.test.tsx +444 -0
- package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsInfra.test.tsx +798 -0
- package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsPlatforms.test.tsx +717 -0
- package/Tests/App/Dashboard/ResourceConnectionGuides.test.ts +981 -0
- package/Tests/App/Dashboard/ResourceConnectionState.test.ts +132 -0
- package/Tests/App/Dashboard/RumDocumentationCard.test.tsx +293 -0
- package/Tests/App/Dashboard/RumSessionReplayRetentionSettings.test.tsx +467 -0
- package/Tests/App/Dashboard/RumSetupGuide.test.ts +758 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +25 -0
- package/Tests/App/Dashboard/ServerlessDocumentationCard.test.tsx +254 -0
- package/Tests/App/Dashboard/ServerlessSetupGuide.test.ts +1074 -0
- package/Tests/App/Dashboard/SetupGuide.test.ts +246 -0
- package/Tests/App/Dashboard/SetupGuideCard.test.tsx +756 -0
- package/Tests/App/Dashboard/SloListSideMenu.test.tsx +120 -0
- package/Tests/App/Dashboard/SsoPages.test.tsx +833 -0
- package/Tests/App/Dashboard/TelemetryDocumentation.test.tsx +253 -0
- package/Tests/App/Dashboard/TelemetryRetentionShells.test.tsx +730 -0
- package/Tests/App/Dashboard/VMwareSetupGuide.test.ts +1033 -0
- package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +87 -7
- package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +28 -9
- package/Tests/Models/DatabaseModels/IncidentFormModel.test.ts +1250 -0
- package/Tests/Models/DatabaseModels/MessageQueueModels.test.ts +1983 -0
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
- package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +190 -14
- package/Tests/Models/IncidentTemplateCustomFieldSettingsColumn.test.ts +291 -0
- package/Tests/Server/API/AIInvestigationConversationAPI.test.ts +412 -0
- package/Tests/Server/API/GlobalConfigLicense.test.ts +38 -9
- package/Tests/Server/API/IncidentFormAPI.test.ts +942 -0
- package/Tests/Server/API/IncidentFormPublicRoutes.test.ts +1653 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -1
- package/Tests/Server/API/{SsoProviderListingsEdition.test.ts → SsoProviderListings.test.ts} +164 -110
- package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +76 -54
- package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +125 -42
- package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +36 -10
- package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +51 -9
- package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +13 -8
- package/Tests/Server/Enterprise/SsoInCommunityEdition.test.ts +540 -0
- package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentFormsMigration.test.ts +438 -0
- package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleNotificationChannelsMigration.test.ts +255 -0
- package/Tests/Server/Infrastructure/Postgres/MessageQueueTablesMigration.test.ts +637 -0
- package/Tests/Server/Middleware/IncidentFormRateLimit.test.ts +1630 -0
- package/Tests/Server/Middleware/UserAuthorization.test.ts +16 -17
- package/Tests/Server/Middleware/UserAuthorizationSsoEnforcement.test.ts +544 -0
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +37 -8
- package/Tests/Server/Services/IncidentCreatedFeedTitle.test.ts +212 -0
- package/Tests/Server/Services/IncidentCustomFieldDeleteFormQuestions.test.ts +297 -0
- package/Tests/Server/Services/IncidentFormOwnersCreatedNotification.test.ts +497 -0
- package/Tests/Server/Services/IncidentFormPostgres.test.ts +958 -0
- package/Tests/Server/Services/IncidentFormPublicForm.test.ts +1260 -0
- package/Tests/Server/Services/IncidentFormPublicSubmission.test.ts +2520 -0
- package/Tests/Server/Services/IncidentFormRegistration.test.ts +159 -0
- package/Tests/Server/Services/IncidentFormService.test.ts +663 -0
- package/Tests/Server/Services/IncidentFormSubmissionService.test.ts +430 -0
- package/Tests/Server/Services/IncidentFormSubmissionTemplateBranch.test.ts +473 -0
- package/Tests/Server/Services/IncidentGroupingTemplateSubstitution.test.ts +81 -0
- package/Tests/Server/Services/IncidentTemplateCustomFieldSettings.test.ts +262 -0
- package/Tests/Server/Services/IncidentTemplateOwnersOnDeclare.test.ts +566 -0
- package/Tests/Server/Services/IncidentTitleMarkdownSinks.test.ts +640 -0
- package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +3 -3
- package/Tests/Server/Services/MessageQueueOwnerServices.test.ts +440 -0
- package/Tests/Server/Services/MessageQueueRegistries.test.ts +577 -0
- package/Tests/Server/Services/MessageQueueRuleEngines.test.ts +900 -0
- package/Tests/Server/Services/MessageQueueService.test.ts +2210 -0
- package/Tests/Server/Services/MessageQueueSqlPostgres.test.ts +1271 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +28 -1
- package/Tests/Server/Services/OpenTelemetryRetentionOverrideEdition.test.ts +474 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +21 -0
- package/Tests/Server/Services/OwnerRowUniqueness.test.ts +3 -0
- package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +0 -1
- package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +56 -6
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -1
- package/Tests/Server/Services/SsoRequirementReads.test.ts +454 -0
- package/Tests/Server/Services/SsoRequirementWrites.test.ts +529 -0
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
- package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +107 -89
- package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +1068 -176
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +1962 -200
- package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +654 -297
- package/Tests/Server/Types/Database/Permissions/RetentionOverrideColumns.test.ts +784 -0
- package/Tests/Server/Types/Database/Permissions/SsoScalePlanGate.test.ts +418 -0
- package/Tests/Server/Utils/AI/AIAlertGating.test.ts +0 -1
- package/Tests/Server/Utils/AI/AIInvestigationEngineRunLimits.test.ts +136 -0
- package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +15 -14
- package/Tests/Server/Utils/AI/AgentContextCompactor.test.ts +351 -0
- package/Tests/Server/Utils/AI/ChatAgentRunnerSharedThread.test.ts +842 -0
- package/Tests/Server/Utils/AI/ChatRunCancellation.test.ts +195 -0
- package/Tests/Server/Utils/AI/CodeFixReadiness.test.ts +10 -57
- package/Tests/Server/Utils/AI/FixRunBudget.test.ts +20 -24
- package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +7 -30
- package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +225 -29
- package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +266 -25
- package/Tests/Server/Utils/AI/InvestigationThread.test.ts +580 -0
- package/Tests/Server/Utils/AI/InvestigationThreadService.test.ts +801 -0
- package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +525 -43
- package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +4 -2
- package/Tests/Server/Utils/AI/LLMServiceUnreachableProvider.test.ts +435 -0
- package/Tests/Server/Utils/AI/ObservabilityAssistantLimits.test.ts +462 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +14 -25
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +2 -0
- package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +297 -15
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +10 -6
- package/Tests/Server/Utils/AI/ToolOutputPager.test.ts +382 -0
- package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +23 -4
- package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +25 -6
- package/Tests/Server/Utils/AI/Toolbox/Serializer.test.ts +14 -3
- package/Tests/Server/Utils/EditionEnforcement.test.ts +107 -147
- package/Tests/Server/Utils/{CommunityEditionSsoReport.test.ts → RelaxedScimTeamLocksReport.test.ts} +212 -208
- package/Tests/Server/Utils/SameOriginRequest.test.ts +372 -0
- package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +20 -0
- package/Tests/Server/Utils/Telemetry/MessageQueueDiscovery.test.ts +2590 -0
- package/Tests/Server/Utils/Telemetry/MessageQueueSpanQueryTwin.ts +246 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +23 -0
- package/Tests/Types/AI/AIAgentRunLimits.test.ts +255 -0
- package/Tests/Types/Code/JsonSyntax.test.ts +1300 -0
- package/Tests/Types/CustomField/CustomFieldCreateSettings.test.ts +1113 -0
- package/Tests/Types/CustomField/CustomFieldMappingCatalog.test.ts +91 -0
- package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +30 -0
- package/Tests/Types/Incident/IncidentFormIpAllowlist.test.ts +227 -0
- package/Tests/Types/Incident/IncidentFormPublic.test.ts +1916 -0
- package/Tests/Types/MessageQueue/MessageQueueEntityKey.test.ts +916 -0
- package/Tests/Types/MessageQueue/MessageQueueIdentity.test.ts +567 -0
- package/Tests/Types/MessageQueue/MessageQueueManualIdentity.test.ts +686 -0
- package/Tests/Types/MessageQueue/MessageQueueMetricCatalog.test.ts +1726 -0
- package/Tests/Types/MessageQueue/MessageQueueMetricResolution.test.ts +1758 -0
- package/Tests/Types/MessageQueue/MessagingInstrumentationFixtures.test.ts +4686 -0
- package/Tests/Types/MessageQueue/MessagingSystem.test.ts +840 -0
- package/Tests/Types/MessageQueue/MessagingTelemetryFixtures.ts +3287 -0
- package/Tests/Types/MessageQueue/MessagingTelemetryResolver.test.ts +2966 -0
- package/Tests/Types/Monitor/MessageQueueAlertTemplates.test.ts +5401 -0
- package/Tests/Types/Monitor/MetricEngineFold.ts +154 -0
- package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +5 -3
- package/Tests/Types/Team/ComplianceRule.test.ts +588 -37
- package/Tests/Types/Team/TeamComplianceStatus.test.ts +64 -11
- package/Tests/Types/XML.test.ts +51 -0
- package/Tests/UI/Components/CodeEditor/CodeEditor.test.tsx +2362 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorCommands.test.ts +810 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorHighlight.test.ts +338 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorLanguages.test.ts +222 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorStyles.test.ts +807 -0
- package/Tests/UI/Components/CodeEditor/NoMonaco.test.ts +245 -0
- package/Tests/UI/Components/CodeEditor/YamlEditor.test.tsx +729 -0
- package/Tests/UI/Components/CopyTextButton.test.tsx +76 -0
- package/Tests/UI/Components/EditionLabelLapseCopy.ts +51 -14
- package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +131 -23
- package/Tests/UI/Components/ErrorMessageAction.test.tsx +142 -0
- package/Tests/UI/Components/FacetSidebarInScope.test.tsx +459 -0
- package/Tests/UI/Components/FacetValuesInScope.test.ts +270 -0
- package/Tests/UI/Components/Forms/FormFieldCodeEditors.test.tsx +294 -0
- package/Tests/UI/Components/Forms/FormFieldMarkdown.test.tsx +126 -0
- package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +22 -75
- package/Tests/UI/Components/Forms/ModelFormOnBeforeCreateMiscData.test.tsx +254 -0
- package/Tests/UI/Components/Icon.test.tsx +32 -0
- package/Tests/UI/Components/Input.test.tsx +31 -0
- package/Tests/UI/Components/LockedFilterChipScope.test.tsx +501 -0
- package/Tests/UI/Components/MarkdownConverters.test.ts +253 -1
- package/Tests/UI/Components/MarkdownEditor.test.tsx +2785 -2
- package/Tests/UI/Components/MarkdownEditorHistory.test.ts +243 -0
- package/Tests/UI/Components/MarkdownListEditing.test.ts +1118 -0
- package/Tests/UI/Components/MarkdownPaste.test.ts +1180 -0
- package/Tests/UI/Components/MarkdownViewerCopy.test.tsx +466 -0
- package/Tests/UI/Components/MarkdownVisualEditing.test.ts +1089 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableEmptyStateAction.test.tsx +623 -0
- package/Tests/UI/Components/ResetObjectID.test.tsx +282 -0
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- package/Tests/UI/Components/Table/EmptyTableMessage.test.ts +439 -0
- package/Tests/UI/Components/Table/TableEmptyState.test.tsx +352 -0
- package/Tests/UI/Components/TableRowMobileTitles.test.tsx +78 -0
- package/Tests/UI/Components/TextArea.test.tsx +19 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +12 -29
- package/Tests/UI/Components/fixtures/MarkdownPasteFixtures.ts +460 -0
- package/Tests/UI/EsbuildConfig.test.ts +115 -39
- package/Tests/UI/EsbuildEnterpriseAlias.test.ts +4 -4
- package/Tests/UI/Monitor/MonitorLogSummaryRender.test.tsx +377 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +81 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +17 -0
- package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
- package/Tests/Utils/Markdown/DashboardMarkdownRenderer.ts +81 -0
- package/Tests/Utils/Markdown/UntrustedMarkdown.test.ts +1339 -0
- package/Tests/Utils/Monitor/MonitorLogSummaryUtil.test.ts +862 -0
- package/Types/AI/AIAgentRunLimits.ts +146 -0
- package/Types/Code/CodeType.ts +2 -0
- package/Types/Code/JsonSyntax.ts +923 -0
- package/Types/CustomField/CustomFieldCreateSettings.ts +549 -0
- package/Types/CustomField/CustomFieldMappingCatalog.ts +77 -0
- package/Types/CustomField/CustomFieldValueValidator.ts +10 -7
- package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +1 -1
- package/Types/Icon/IconProp.ts +2 -0
- package/Types/Incident/IncidentFormIpAllowlist.ts +206 -0
- package/Types/Incident/IncidentFormPublic.ts +1145 -0
- package/Types/Kubernetes/KubernetesClusterAiAccess.ts +10 -3
- package/Types/MessageQueue/MessageQueueIdentity.ts +256 -0
- package/Types/MessageQueue/MessageQueueManualIdentity.ts +325 -0
- package/Types/MessageQueue/MessageQueueMetricCatalog.ts +1357 -0
- package/Types/MessageQueue/MessagingSystem.ts +518 -0
- package/Types/MessageQueue/MessagingTelemetryResolver.ts +2311 -0
- package/Types/Monitor/MessageQueueAlertTemplates.ts +2128 -0
- package/Types/Permission.ts +310 -0
- package/Types/ResourceAiAgent/ResourceAiAccess.ts +10 -4
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +12 -0
- package/Types/Rules/RuleRun.ts +4 -0
- package/Types/Team/ComplianceNotificationChannel.ts +4 -3
- package/Types/Team/ComplianceRule.ts +71 -18
- package/Types/Team/ComplianceRuleType.ts +2 -2
- package/Types/Team/TeamComplianceStatus.ts +6 -2
- package/Types/Telemetry/EntityType.ts +16 -0
- package/Types/Telemetry/LockedFilterDetail.ts +22 -0
- package/Types/XML.ts +26 -0
- package/UI/Components/CodeEditor/CodeEditor.css +379 -0
- package/UI/Components/CodeEditor/CodeEditor.tsx +1128 -294
- package/UI/Components/CodeEditor/CodeEditorCommands.ts +774 -0
- package/UI/Components/CodeEditor/CodeEditorHighlight.ts +106 -0
- package/UI/Components/CodeEditor/CodeEditorLanguages.ts +164 -0
- package/UI/Components/CodeEditor/YamlEditor.tsx +28 -326
- package/UI/Components/CopyTextButton/CopyTextButton.tsx +5 -1
- package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +6 -25
- package/UI/Components/EditionLabel/EditionLabel.tsx +14 -13
- package/UI/Components/ErrorMessage/ErrorMessage.tsx +11 -1
- package/UI/Components/Forms/Fields/FormField.tsx +8 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Icon/Icon.tsx +18 -0
- package/UI/Components/Input/Input.tsx +11 -4
- package/UI/Components/LogsViewer/LogsViewer.tsx +4 -0
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +21 -3
- package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
- package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +877 -79
- package/UI/Components/Markdown.tsx/MarkdownEditorHistory.ts +284 -0
- package/UI/Components/Markdown.tsx/MarkdownListEditing.ts +1362 -0
- package/UI/Components/Markdown.tsx/MarkdownPaste.ts +1326 -0
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +106 -10
- package/UI/Components/Markdown.tsx/MarkdownVisualEditing.ts +1032 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +85 -7
- package/UI/Components/ResetObjectID/ResetObjectID.tsx +35 -9
- package/UI/Components/Table/EmptyTableMessage.ts +81 -0
- package/UI/Components/Table/Table.tsx +50 -21
- package/UI/Components/Table/TableRow.tsx +9 -1
- package/UI/Components/TelemetryViewer/FacetVisibility.ts +97 -2
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +13 -0
- package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +214 -7
- package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +21 -3
- package/UI/Components/TextArea/TextArea.tsx +9 -2
- package/UI/Components/Workflow/ModelFieldPicker.tsx +10 -12
- package/UI/Styles/Theme.css +8 -0
- package/UI/esbuild-config.js +2 -32
- package/Utils/LabelRuleImportExport.ts +2 -0
- package/Utils/Markdown/UntrustedMarkdown.ts +1513 -0
- package/Utils/Monitor/IncomingEmailMonitorRequestUtil.ts +41 -0
- package/Utils/Monitor/MonitorLogSummaryUtil.ts +212 -0
- package/Utils/Telemetry/EntityKey.ts +48 -0
- package/build/dist/Models/DatabaseModels/AIConversation.js +125 -1
- package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CloudResource.js +12 -0
- package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseServer.js +12 -0
- package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +12 -0
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidc.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSso.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Host.js +12 -0
- package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentForm.js +605 -0
- package/build/dist/Models/DatabaseModels/IncidentForm.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js +299 -0
- package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js +60 -0
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +14 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js +12 -0
- package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MessageQueue.js +1106 -0
- package/build/dist/Models/DatabaseModels/MessageQueue.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js +562 -0
- package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js +643 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js +506 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js +505 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/PodmanHost.js +12 -0
- package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +61 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectOidc.js +0 -4
- package/build/dist/Models/DatabaseModels/ProjectOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectSso.js +0 -4
- package/build/dist/Models/DatabaseModels/ProjectSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +12 -0
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServerlessFunction.js +12 -0
- package/build/dist/Models/DatabaseModels/ServerlessFunction.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Service.js +12 -0
- package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js +0 -4
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageSso.js +0 -4
- package/build/dist/Models/DatabaseModels/StatusPageSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +49 -3
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js +12 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +9 -103
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +3 -3
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationConversationAPI.js +162 -0
- package/build/dist/Server/API/AIInvestigationConversationAPI.js.map +1 -0
- package/build/dist/Server/API/IncidentFormAPI.js +245 -0
- package/build/dist/Server/API/IncidentFormAPI.js.map +1 -0
- package/build/dist/Server/API/ProjectOIDC.js +16 -24
- package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
- package/build/dist/Server/API/ProjectSSO.js +16 -24
- package/build/dist/Server/API/ProjectSSO.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +42 -75
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseEdition.js +79 -25
- package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseFeature.js +24 -3
- package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +1 -1
- package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js +63 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js +41 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js +183 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/IncidentFormRateLimit.js +559 -0
- package/build/dist/Server/Middleware/IncidentFormRateLimit.js.map +1 -0
- package/build/dist/Server/Middleware/UserAuthorization.js +11 -30
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +10 -13
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/IncidentCustomFieldService.js +83 -0
- package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js +8 -4
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +10 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentFormService.js +1202 -0
- package/build/dist/Server/Services/IncidentFormService.js.map +1 -0
- package/build/dist/Server/Services/IncidentFormSubmissionService.js +91 -0
- package/build/dist/Server/Services/IncidentFormSubmissionService.js.map +1 -0
- package/build/dist/Server/Services/IncidentGroupingEngineService.js +20 -8
- package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +106 -4
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +18 -0
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +14 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js +262 -0
- package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueLabelRuleService.js +13 -0
- package/build/dist/Server/Services/MessageQueueLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js +334 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleService.js +13 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerTeamService.js +62 -0
- package/build/dist/Server/Services/MessageQueueOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerUserService.js +62 -0
- package/build/dist/Server/Services/MessageQueueOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueService.js +1140 -0
- package/build/dist/Server/Services/MessageQueueService.js.map +1 -0
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +25 -4
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +0 -54
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -4
- package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +0 -46
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceSettingService.js +120 -31
- package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +8 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +12 -3
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +6 -3
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +109 -37
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +16 -0
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js +3 -3
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +9 -6
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js +128 -0
- package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +192 -50
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js +133 -0
- package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +53 -13
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
- package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js +206 -0
- package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js.map +1 -0
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +53 -26
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +4 -2
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +1 -29
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +24 -24
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +7 -2
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +29 -14
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +38 -14
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +2 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +40 -10
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +19 -7
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +19 -7
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +15 -18
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js +412 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js +495 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js +10 -3
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -1
- package/build/dist/Server/Utils/EditionEnforcement.js +20 -158
- package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +193 -54
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js +160 -0
- package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js.map +1 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
- package/build/dist/Server/Utils/SameOriginRequest.js +151 -0
- package/build/dist/Server/Utils/SameOriginRequest.js.map +1 -0
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +4 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js +1015 -0
- package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +4 -1
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
- package/build/dist/Server/Utils/VendorAssets.js +3 -3
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +3 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +3 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +10 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/AI/AIAgentRunLimits.js +91 -0
- package/build/dist/Types/AI/AIAgentRunLimits.js.map +1 -0
- package/build/dist/Types/Code/CodeType.js +2 -0
- package/build/dist/Types/Code/CodeType.js.map +1 -1
- package/build/dist/Types/Code/JsonSyntax.js +604 -0
- package/build/dist/Types/Code/JsonSyntax.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldCreateSettings.js +310 -0
- package/build/dist/Types/CustomField/CustomFieldCreateSettings.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js +38 -0
- package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js +10 -7
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -1
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +1 -1
- package/build/dist/Types/Icon/IconProp.js +2 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/Incident/IncidentFormIpAllowlist.js +119 -0
- package/build/dist/Types/Incident/IncidentFormIpAllowlist.js.map +1 -0
- package/build/dist/Types/Incident/IncidentFormPublic.js +678 -0
- package/build/dist/Types/Incident/IncidentFormPublic.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +10 -3
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
- package/build/dist/Types/MessageQueue/MessageQueueIdentity.js +184 -0
- package/build/dist/Types/MessageQueue/MessageQueueIdentity.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js +190 -0
- package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js +1057 -0
- package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessagingSystem.js +394 -0
- package/build/dist/Types/MessageQueue/MessagingSystem.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js +1768 -0
- package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js.map +1 -0
- package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js +1207 -0
- package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js.map +1 -0
- package/build/dist/Types/Permission.js +274 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +10 -4
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +12 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +4 -0
- package/build/dist/Types/Rules/RuleRun.js.map +1 -1
- package/build/dist/Types/Team/ComplianceNotificationChannel.js +4 -3
- package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -1
- package/build/dist/Types/Team/ComplianceRule.js +48 -14
- package/build/dist/Types/Team/ComplianceRule.js.map +1 -1
- package/build/dist/Types/Team/ComplianceRuleType.js +2 -2
- package/build/dist/Types/Telemetry/EntityType.js +16 -0
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/XML.js +23 -0
- package/build/dist/Types/XML.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +641 -266
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js +464 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js.map +1 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js +69 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js.map +1 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js +108 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js.map +1 -0
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js +14 -186
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -1
- package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +5 -1
- package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +5 -14
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +14 -13
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js +1 -1
- package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +8 -3
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +8 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +11 -3
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +2 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +14 -4
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +710 -65
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js +238 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js +1028 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js +1088 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +70 -10
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js +887 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +57 -6
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js +7 -5
- package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js.map +1 -1
- package/build/dist/UI/Components/Table/EmptyTableMessage.js +51 -0
- package/build/dist/UI/Components/Table/EmptyTableMessage.js.map +1 -0
- package/build/dist/UI/Components/Table/Table.js +30 -11
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +9 -2
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js +63 -1
- package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +4 -2
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +91 -10
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +14 -4
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js +9 -3
- package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ModelFieldPicker.js +1 -2
- package/build/dist/UI/Components/Workflow/ModelFieldPicker.js.map +1 -1
- package/build/dist/Utils/LabelRuleImportExport.js +2 -0
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/Utils/Markdown/UntrustedMarkdown.js +988 -0
- package/build/dist/Utils/Markdown/UntrustedMarkdown.js.map +1 -0
- package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js +34 -0
- package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js +148 -0
- package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js.map +1 -0
- package/build/dist/Utils/Telemetry/EntityKey.js +36 -0
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- package/package.json +1 -3
- package/Server/Utils/CommunityEditionSsoReport.ts +0 -361
- package/Tests/App/Accounts/LoginSsoEdition.test.tsx +0 -616
- package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +0 -217
- package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +0 -271
- package/Tests/App/Dashboard/CloudDocumentationMarkdown.test.ts +0 -215
- package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +0 -784
- package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +0 -518
- package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +0 -419
- package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +0 -769
- package/Tests/UI/Components/CodeEditor.test.tsx +0 -638
- package/Tests/UI/Components/MonacoLoader.test.ts +0 -67
- package/Tests/UI/Components/MonacoRuntime.test.ts +0 -121
- package/Tests/UI/Components/Telemetry/TelemetryRetentionConfigForm.test.tsx +0 -329
- package/Tests/UI/Components/YamlEditor.test.tsx +0 -847
- package/UI/Components/CodeEditor/MonacoLoader.ts +0 -36
- package/UI/Components/Telemetry/TelemetryRetentionConfigForm.tsx +0 -319
- package/UI/Components/Telemetry/TelemetryRetentionConfigSummary.tsx +0 -223
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js +0 -223
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +0 -1
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +0 -34
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +0 -1
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js +0 -171
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js.map +0 -1
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js +0 -119
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js.map +0 -1
|
@@ -0,0 +1,1145 @@
|
|
|
1
|
+
import { parseCustomFieldDropdownOptions } from "../CustomField/CustomFieldDropdownOption";
|
|
2
|
+
import CustomFieldType from "../CustomField/CustomFieldType";
|
|
3
|
+
import { isCustomFieldValueEmpty } from "../CustomField/CustomFieldValueMapping";
|
|
4
|
+
import {
|
|
5
|
+
CustomFieldValueValidationError,
|
|
6
|
+
validateCustomFieldValues,
|
|
7
|
+
} from "../CustomField/CustomFieldValueValidator";
|
|
8
|
+
import ColumnLength from "../Database/ColumnLength";
|
|
9
|
+
import Email from "../Email";
|
|
10
|
+
import { JSONObject, JSONValue } from "../JSON";
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
* The public half of incident forms: what the page anyone with a form's link
|
|
14
|
+
* opens is told about the form, what it sends back, and the one set of rules
|
|
15
|
+
* a submission is held to.
|
|
16
|
+
*
|
|
17
|
+
* A form is filled in by people with no OneUptime account, so nothing on the
|
|
18
|
+
* way in is trusted. The server builds the incident only from what
|
|
19
|
+
* validateIncidentFormSubmission hands back - a value assembled field by
|
|
20
|
+
* field from the answers the form actually asks for. Anything else in the
|
|
21
|
+
* request (a projectId, a state, isPrivate, an answer to a field the form
|
|
22
|
+
* does not ask) never reaches it. Every text is trimmed and capped so it fits
|
|
23
|
+
* the column it lands in, and every custom field answer is checked with the
|
|
24
|
+
* same rules as an API write (validateCustomFieldValues) plus the form's own
|
|
25
|
+
* Required.
|
|
26
|
+
*
|
|
27
|
+
* Pure, with no database or React imports: the server enforces these rules,
|
|
28
|
+
* and the public page can show the same limits before anything is sent.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/*
|
|
32
|
+
* How the form treats one of its built-in questions. Only the description
|
|
33
|
+
* has a setting today: Title is always asked and always required.
|
|
34
|
+
*/
|
|
35
|
+
export enum IncidentFormFieldSetting {
|
|
36
|
+
Required = "Required",
|
|
37
|
+
Optional = "Optional",
|
|
38
|
+
Hidden = "Hidden",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Every setting, in the order a picker lists them.
|
|
42
|
+
export const INCIDENT_FORM_FIELD_SETTINGS: ReadonlyArray<IncidentFormFieldSetting> =
|
|
43
|
+
[
|
|
44
|
+
IncidentFormFieldSetting.Required,
|
|
45
|
+
IncidentFormFieldSetting.Optional,
|
|
46
|
+
IncidentFormFieldSetting.Hidden,
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
// What a form asks for the description when nobody chose otherwise.
|
|
50
|
+
export const DEFAULT_INCIDENT_FORM_DESCRIPTION_SETTING: IncidentFormFieldSetting =
|
|
51
|
+
IncidentFormFieldSetting.Optional;
|
|
52
|
+
|
|
53
|
+
export type IsIncidentFormFieldSettingFunction = (
|
|
54
|
+
value: unknown,
|
|
55
|
+
) => value is IncidentFormFieldSetting;
|
|
56
|
+
|
|
57
|
+
/** Whether a value is one of the settings, spelled exactly as stored. */
|
|
58
|
+
export const isIncidentFormFieldSetting: IsIncidentFormFieldSettingFunction = (
|
|
59
|
+
value: unknown,
|
|
60
|
+
): value is IncidentFormFieldSetting => {
|
|
61
|
+
return (
|
|
62
|
+
typeof value === "string" &&
|
|
63
|
+
(INCIDENT_FORM_FIELD_SETTINGS as ReadonlyArray<string>).includes(value)
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
* The longest answers a form takes. Each is at most the column the answer is
|
|
69
|
+
* stored in, so a submission that passes here cannot then fail to save:
|
|
70
|
+
*
|
|
71
|
+
* - the title becomes Incident.title, a varchar(500);
|
|
72
|
+
* - the description becomes Incident.description (text) - capped anyway,
|
|
73
|
+
* since the request body may be up to 50 MB and an incident description
|
|
74
|
+
* is read in emails, Slack and on status pages;
|
|
75
|
+
* - each custom field text answer is stored in Incident.customFields;
|
|
76
|
+
* - the reporter's name and email are stored on IncidentFormSubmission, in
|
|
77
|
+
* a varchar(100) each (an email longer than that is not one anybody can
|
|
78
|
+
* sign up to OneUptime with either).
|
|
79
|
+
*/
|
|
80
|
+
export const INCIDENT_FORM_TITLE_MAX_LENGTH: number = ColumnLength.LongText;
|
|
81
|
+
export const INCIDENT_FORM_DESCRIPTION_MAX_LENGTH: number = 20000;
|
|
82
|
+
export const INCIDENT_FORM_CUSTOM_FIELD_TEXT_MAX_LENGTH: number = 10000;
|
|
83
|
+
export const INCIDENT_FORM_REPORTER_NAME_MAX_LENGTH: number =
|
|
84
|
+
ColumnLength.ShortText;
|
|
85
|
+
export const INCIDENT_FORM_REPORTER_EMAIL_MAX_LENGTH: number =
|
|
86
|
+
ColumnLength.Email;
|
|
87
|
+
|
|
88
|
+
/*
|
|
89
|
+
* The most entries a multi-select answer may have: this many, or the
|
|
90
|
+
* field's number of options when it has more. The page sends each option
|
|
91
|
+
* at most once, so a real answer never comes near it; what it bounds is a
|
|
92
|
+
* request that sends a million entries, each of which would otherwise be
|
|
93
|
+
* cleaned, sorted and quoted back in the refusal (or, for a field nobody
|
|
94
|
+
* gave options to, stored on the incident).
|
|
95
|
+
*/
|
|
96
|
+
export const INCIDENT_FORM_MULTI_SELECT_MAX_CHOICES: number = 100;
|
|
97
|
+
|
|
98
|
+
/*
|
|
99
|
+
* The titles of the built-in questions. The public page labels its fields
|
|
100
|
+
* with them (through its locale files) and the server names them in its
|
|
101
|
+
* error messages, so both say "Your Email is required." about the same box.
|
|
102
|
+
*/
|
|
103
|
+
export interface IncidentFormQuestionLabels {
|
|
104
|
+
title: string;
|
|
105
|
+
description: string;
|
|
106
|
+
severity: string;
|
|
107
|
+
reporterName: string;
|
|
108
|
+
reporterEmail: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const INCIDENT_FORM_QUESTION_LABELS: IncidentFormQuestionLabels = {
|
|
112
|
+
title: "Title",
|
|
113
|
+
description: "Description",
|
|
114
|
+
severity: "Severity",
|
|
115
|
+
reporterName: "Your Name",
|
|
116
|
+
reporterEmail: "Your Email",
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/*
|
|
120
|
+
* The message templates, worded as the form's own client-side checks word
|
|
121
|
+
* them (Forms/Validation and CUSTOM_FIELD_MUST_BE_CHECKED_MESSAGE), so an
|
|
122
|
+
* answer refused by the server reads like one refused in the browser.
|
|
123
|
+
*/
|
|
124
|
+
const REQUIRED_MESSAGE: string = "{{field}} is required.";
|
|
125
|
+
const TOO_LONG_MESSAGE: string =
|
|
126
|
+
"{{field}} cannot be more than {{maxLength}} characters.";
|
|
127
|
+
const MUST_BE_CHECKED_MESSAGE: string = "{{field}} must be checked.";
|
|
128
|
+
const TOO_MANY_CHOICES_MESSAGE: string =
|
|
129
|
+
"{{field}} cannot have more than {{maxLength}} choices.";
|
|
130
|
+
const NOT_A_LIST_MESSAGE: string = "{{field}} takes one answer, not a list.";
|
|
131
|
+
const NESTED_CHOICE_MESSAGE: string =
|
|
132
|
+
"{{field}} takes a list of its options, not lists or objects within it.";
|
|
133
|
+
const OBJECT_ANSWER_MESSAGE: string =
|
|
134
|
+
"{{field}} takes one answer, not an object.";
|
|
135
|
+
const OBJECT_CHOICES_MESSAGE: string =
|
|
136
|
+
"{{field}} takes a list of its options, not an object.";
|
|
137
|
+
|
|
138
|
+
type FillMessageFunction = (
|
|
139
|
+
template: string,
|
|
140
|
+
values: { field: string; maxLength?: number },
|
|
141
|
+
) => string;
|
|
142
|
+
|
|
143
|
+
/*
|
|
144
|
+
* Replacer functions, not replacement strings: a field named "Cost ($)" or
|
|
145
|
+
* "$& total" must come out as typed, and a replacement string would read its
|
|
146
|
+
* "$" patterns.
|
|
147
|
+
*/
|
|
148
|
+
const fillMessage: FillMessageFunction = (
|
|
149
|
+
template: string,
|
|
150
|
+
values: { field: string; maxLength?: number },
|
|
151
|
+
): string => {
|
|
152
|
+
return template
|
|
153
|
+
.replace("{{field}}", (): string => {
|
|
154
|
+
return values.field;
|
|
155
|
+
})
|
|
156
|
+
.replace("{{maxLength}}", (): string => {
|
|
157
|
+
return String(values.maxLength ?? "");
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/*
|
|
162
|
+
* A header the public page's own script adds to every request it makes, and
|
|
163
|
+
* the value it gives it. Reading a form needs it: another site's page can
|
|
164
|
+
* have a visitor's browser send that GET - an <img>, a link, a no-cors
|
|
165
|
+
* fetch - carrying neither of the headers that say where a request came
|
|
166
|
+
* from (over plain HTTP a browser sends neither on such a request), but it
|
|
167
|
+
* cannot add a header of its own to one. A request that adds one is
|
|
168
|
+
* preflighted first, and then carries the Origin that gives it away
|
|
169
|
+
* (SameOriginRequest). The name is in lower case, as the server reads
|
|
170
|
+
* header names.
|
|
171
|
+
*/
|
|
172
|
+
export const INCIDENT_FORM_PAGE_HEADER: string = "x-oneuptime-incident-form";
|
|
173
|
+
export const INCIDENT_FORM_PAGE_HEADER_VALUE: string = "1";
|
|
174
|
+
|
|
175
|
+
/*
|
|
176
|
+
* One custom field the form asks for, as the public page renders it. Only
|
|
177
|
+
* what the page needs to draw the input: never the field's id, its template
|
|
178
|
+
* variable key, its project-wide switches or its mapping, and a dropdown's
|
|
179
|
+
* options only for a dropdown.
|
|
180
|
+
*/
|
|
181
|
+
export interface PublicIncidentFormField {
|
|
182
|
+
name: string;
|
|
183
|
+
description?: string | undefined;
|
|
184
|
+
customFieldType: CustomFieldType;
|
|
185
|
+
dropdownOptions?: string | undefined;
|
|
186
|
+
isRequired: boolean;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// A severity the reporter can pick, when the form lets them pick one.
|
|
190
|
+
export interface PublicIncidentFormSeverity {
|
|
191
|
+
_id: string;
|
|
192
|
+
name: string;
|
|
193
|
+
color?: string | undefined;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Everything the public page is told about a form.
|
|
197
|
+
export interface PublicIncidentForm {
|
|
198
|
+
name: string;
|
|
199
|
+
// Shown at the top of the page, as Markdown.
|
|
200
|
+
description?: string | undefined;
|
|
201
|
+
descriptionSetting: IncidentFormFieldSetting;
|
|
202
|
+
isReporterDetailsRequired: boolean;
|
|
203
|
+
// Only when the form lets the reporter choose the severity.
|
|
204
|
+
severities?: Array<PublicIncidentFormSeverity> | undefined;
|
|
205
|
+
// The form's own severity, to preselect in that list.
|
|
206
|
+
defaultIncidentSeverityId?: string | undefined;
|
|
207
|
+
// The custom fields the form asks for, in the order to show them.
|
|
208
|
+
customFields: Array<PublicIncidentFormField>;
|
|
209
|
+
isCaptchaRequired: boolean;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/*
|
|
213
|
+
* The answers, as the public page sends them. Custom field answers are keyed
|
|
214
|
+
* by the field's name, as PublicIncidentFormField names it.
|
|
215
|
+
*/
|
|
216
|
+
export interface PublicIncidentFormSubmissionData {
|
|
217
|
+
title: string;
|
|
218
|
+
description?: string | undefined;
|
|
219
|
+
incidentSeverityId?: string | undefined;
|
|
220
|
+
reporterName?: string | undefined;
|
|
221
|
+
reporterEmail?: string | undefined;
|
|
222
|
+
customFields?: JSONObject | undefined;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// The body of the submit request.
|
|
226
|
+
export interface PublicIncidentFormSubmissionRequest {
|
|
227
|
+
data: PublicIncidentFormSubmissionData;
|
|
228
|
+
captchaToken?: string | undefined;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// What the reporter is shown once the incident is declared.
|
|
232
|
+
export interface PublicIncidentFormSubmissionResult {
|
|
233
|
+
// The incident's number as the project shows it, e.g. "INC-42" or "#42".
|
|
234
|
+
incidentNumber?: string | undefined;
|
|
235
|
+
// The form's thank-you text, as Markdown.
|
|
236
|
+
successMessage?: string | undefined;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/*
|
|
240
|
+
* The form's settings a submission is checked against. Every property is
|
|
241
|
+
* optional so an IncidentForm read from the database can be passed as it is;
|
|
242
|
+
* a setting that was not read falls back to the column's default, and the
|
|
243
|
+
* two switches fall back to their stricter side.
|
|
244
|
+
*/
|
|
245
|
+
export interface IncidentFormSubmissionRules {
|
|
246
|
+
descriptionSetting?: IncidentFormFieldSetting | string | null | undefined;
|
|
247
|
+
isReporterDetailsRequired?: boolean | null | undefined;
|
|
248
|
+
allowReporterToChooseSeverity?: boolean | null | undefined;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/*
|
|
252
|
+
* A custom field the form asks for: one of getIncidentFormAskedDefinitions'
|
|
253
|
+
* results, whose isRequiredOnCreate says whether the FORM requires it.
|
|
254
|
+
* Loose enough that IncidentCustomField rows can be passed as they are.
|
|
255
|
+
*/
|
|
256
|
+
export interface IncidentFormAskedDefinition {
|
|
257
|
+
name?: string | null | undefined;
|
|
258
|
+
description?: string | null | undefined;
|
|
259
|
+
customFieldType?: string | null | undefined;
|
|
260
|
+
dropdownOptions?: string | null | undefined;
|
|
261
|
+
isRequiredOnCreate?: boolean | null | undefined;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// A submission that passed: what the incident may be built from.
|
|
265
|
+
export interface ValidatedIncidentFormSubmission {
|
|
266
|
+
title: string;
|
|
267
|
+
// Only when the form asks for it and the reporter wrote something.
|
|
268
|
+
description?: string | undefined;
|
|
269
|
+
// Only when the form lets the reporter choose, and they chose.
|
|
270
|
+
incidentSeverityId?: string | undefined;
|
|
271
|
+
reporterName?: string | undefined;
|
|
272
|
+
// Lowercased, as Email stores it.
|
|
273
|
+
reporterEmail?: string | undefined;
|
|
274
|
+
/*
|
|
275
|
+
* The answers to the fields the form asks for, keyed by field name. An
|
|
276
|
+
* answer left empty is left out, so a template's value for that field
|
|
277
|
+
* still applies (see mergeTemplateCustomFields).
|
|
278
|
+
*/
|
|
279
|
+
customFields: JSONObject;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export type IncidentFormSubmissionValidationResult =
|
|
283
|
+
| {
|
|
284
|
+
isValid: false;
|
|
285
|
+
errors: Array<string>;
|
|
286
|
+
}
|
|
287
|
+
| {
|
|
288
|
+
isValid: true;
|
|
289
|
+
value: ValidatedIncidentFormSubmission;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
type IsPlainObjectFunction = (
|
|
293
|
+
value: unknown,
|
|
294
|
+
) => value is Record<string, unknown>;
|
|
295
|
+
|
|
296
|
+
const isPlainObject: IsPlainObjectFunction = (
|
|
297
|
+
value: unknown,
|
|
298
|
+
): value is Record<string, unknown> => {
|
|
299
|
+
return (
|
|
300
|
+
value !== null &&
|
|
301
|
+
typeof value === "object" &&
|
|
302
|
+
!Array.isArray(value) &&
|
|
303
|
+
!(value instanceof Date)
|
|
304
|
+
);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
type HasOwnFunction = (target: Record<string, unknown>, key: string) => boolean;
|
|
308
|
+
|
|
309
|
+
const hasOwn: HasOwnFunction = (
|
|
310
|
+
target: Record<string, unknown>,
|
|
311
|
+
key: string,
|
|
312
|
+
): boolean => {
|
|
313
|
+
return Object.prototype.hasOwnProperty.call(target, key);
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
/*
|
|
317
|
+
* Postgres cannot store a NUL character in a text column, nor in a jsonb
|
|
318
|
+
* string, so one pasted into an answer would fail the insert - after the
|
|
319
|
+
* checks here had passed. Nobody means to type one. no-control-regex is
|
|
320
|
+
* disabled for these two on purpose: matching control characters is exactly
|
|
321
|
+
* what they are for.
|
|
322
|
+
*/
|
|
323
|
+
// eslint-disable-next-line no-control-regex
|
|
324
|
+
const NUL_CHARACTERS: RegExp = /\u0000/g;
|
|
325
|
+
|
|
326
|
+
// Control characters, line breaks and tabs included.
|
|
327
|
+
// eslint-disable-next-line no-control-regex
|
|
328
|
+
const CONTROL_CHARACTER_RUNS: RegExp = /[\u0000-\u001F\u007F]+/g;
|
|
329
|
+
|
|
330
|
+
type CleanTextFunction = (value: string) => string;
|
|
331
|
+
|
|
332
|
+
/*
|
|
333
|
+
* A one-line answer (the title, the reporter's name): line breaks, tabs and
|
|
334
|
+
* other control characters become spaces - a title is shown on one line in
|
|
335
|
+
* lists, emails and chat messages - runs of spaces become one, and the ends
|
|
336
|
+
* are trimmed.
|
|
337
|
+
*/
|
|
338
|
+
const cleanSingleLine: CleanTextFunction = (value: string): string => {
|
|
339
|
+
return value
|
|
340
|
+
.replace(CONTROL_CHARACTER_RUNS, " ")
|
|
341
|
+
.replace(/ {2,}/g, " ")
|
|
342
|
+
.trim();
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
/*
|
|
346
|
+
* A Markdown or multi-line answer. Blank lines before the text and
|
|
347
|
+
* whitespace after it are dropped, but the first line keeps its own
|
|
348
|
+
* indentation: four spaces there start a code block, and trimming them would
|
|
349
|
+
* turn the reporter's pasted log into a paragraph.
|
|
350
|
+
*/
|
|
351
|
+
const cleanMultiLine: CleanTextFunction = (value: string): string => {
|
|
352
|
+
return value
|
|
353
|
+
.replace(NUL_CHARACTERS, "")
|
|
354
|
+
.replace(/^\s*\n/, "")
|
|
355
|
+
.trimEnd();
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
// A dropdown option, a number or a date typed as text.
|
|
359
|
+
const cleanScalarText: CleanTextFunction = (value: string): string => {
|
|
360
|
+
return value.replace(NUL_CHARACTERS, "").trim();
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
type NormalizedFieldFunction = (
|
|
364
|
+
definition: IncidentFormAskedDefinition,
|
|
365
|
+
) => PublicIncidentFormField | null;
|
|
366
|
+
|
|
367
|
+
const KNOWN_CUSTOM_FIELD_TYPES: ReadonlyArray<string> =
|
|
368
|
+
Object.values(CustomFieldType);
|
|
369
|
+
|
|
370
|
+
const DROPDOWN_TYPES: ReadonlyArray<CustomFieldType> = [
|
|
371
|
+
CustomFieldType.Dropdown,
|
|
372
|
+
CustomFieldType.MultiSelectDropdown,
|
|
373
|
+
];
|
|
374
|
+
|
|
375
|
+
/*
|
|
376
|
+
* One asked definition as the public page sees it and as its answers are
|
|
377
|
+
* checked. A field with no type - rows from before types were required - or
|
|
378
|
+
* a type this version does not know is asked and checked as Text: the admin
|
|
379
|
+
* put it on the form, and text is what an untyped field has always held.
|
|
380
|
+
*/
|
|
381
|
+
const toPublicField: NormalizedFieldFunction = (
|
|
382
|
+
definition: IncidentFormAskedDefinition,
|
|
383
|
+
): PublicIncidentFormField | null => {
|
|
384
|
+
if (
|
|
385
|
+
!definition ||
|
|
386
|
+
typeof definition.name !== "string" ||
|
|
387
|
+
definition.name.length === 0
|
|
388
|
+
) {
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const customFieldType: CustomFieldType =
|
|
393
|
+
typeof definition.customFieldType === "string" &&
|
|
394
|
+
KNOWN_CUSTOM_FIELD_TYPES.includes(definition.customFieldType)
|
|
395
|
+
? (definition.customFieldType as CustomFieldType)
|
|
396
|
+
: CustomFieldType.Text;
|
|
397
|
+
|
|
398
|
+
const field: PublicIncidentFormField = {
|
|
399
|
+
name: definition.name,
|
|
400
|
+
customFieldType: customFieldType,
|
|
401
|
+
isRequired: definition.isRequiredOnCreate === true,
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
if (
|
|
405
|
+
typeof definition.description === "string" &&
|
|
406
|
+
definition.description.trim().length > 0
|
|
407
|
+
) {
|
|
408
|
+
field.description = definition.description;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (
|
|
412
|
+
DROPDOWN_TYPES.includes(customFieldType) &&
|
|
413
|
+
typeof definition.dropdownOptions === "string" &&
|
|
414
|
+
definition.dropdownOptions.trim().length > 0
|
|
415
|
+
) {
|
|
416
|
+
field.dropdownOptions = definition.dropdownOptions;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return field;
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
export type GetPublicIncidentFormFieldsFunction = (
|
|
423
|
+
askedDefinitions: Array<IncidentFormAskedDefinition>,
|
|
424
|
+
) => Array<PublicIncidentFormField>;
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* The custom fields the public page shows, in the order given (pass the
|
|
428
|
+
* result of getIncidentFormAskedDefinitions). A definition without a name is
|
|
429
|
+
* left out, and so is a second one with a name already listed - answers are
|
|
430
|
+
* keyed by name, so it could not be told apart.
|
|
431
|
+
*/
|
|
432
|
+
export const getPublicIncidentFormFields: GetPublicIncidentFormFieldsFunction =
|
|
433
|
+
(
|
|
434
|
+
askedDefinitions: Array<IncidentFormAskedDefinition>,
|
|
435
|
+
): Array<PublicIncidentFormField> => {
|
|
436
|
+
const fields: Array<PublicIncidentFormField> = [];
|
|
437
|
+
const seenNames: Set<string> = new Set<string>();
|
|
438
|
+
|
|
439
|
+
for (const definition of askedDefinitions || []) {
|
|
440
|
+
const field: PublicIncidentFormField | null = toPublicField(definition);
|
|
441
|
+
|
|
442
|
+
if (!field || seenNames.has(field.name)) {
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
seenNames.add(field.name);
|
|
447
|
+
fields.push(field);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return fields;
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
type ReadDescriptionSettingFunction = (
|
|
454
|
+
value: unknown,
|
|
455
|
+
) => IncidentFormFieldSetting;
|
|
456
|
+
|
|
457
|
+
// A stored setting, or the column's default for anything else.
|
|
458
|
+
const readDescriptionSetting: ReadDescriptionSettingFunction = (
|
|
459
|
+
value: unknown,
|
|
460
|
+
): IncidentFormFieldSetting => {
|
|
461
|
+
return isIncidentFormFieldSetting(value)
|
|
462
|
+
? value
|
|
463
|
+
: DEFAULT_INCIDENT_FORM_DESCRIPTION_SETTING;
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
/*
|
|
467
|
+
* The form, as far as the public page is concerned. Loose for the same
|
|
468
|
+
* reason as IncidentFormSubmissionRules: an IncidentForm row fits.
|
|
469
|
+
*/
|
|
470
|
+
export interface PublicIncidentFormSource extends IncidentFormSubmissionRules {
|
|
471
|
+
name?: string | null | undefined;
|
|
472
|
+
description?: string | null | undefined;
|
|
473
|
+
incidentSeverityId?: { toString: () => string } | string | null | undefined;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export type GetPublicIncidentFormFunction = (data: {
|
|
477
|
+
form: PublicIncidentFormSource;
|
|
478
|
+
askedDefinitions: Array<IncidentFormAskedDefinition>;
|
|
479
|
+
// The project's severities, in the order to list them.
|
|
480
|
+
severities: Array<PublicIncidentFormSeverity>;
|
|
481
|
+
isCaptchaRequired: boolean;
|
|
482
|
+
}) => PublicIncidentForm;
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* What the public page is told about a form, built field by field so that
|
|
486
|
+
* nothing else about the form - its share key, its template, its IP
|
|
487
|
+
* allowlist, its project - is ever sent. The project's severities are only
|
|
488
|
+
* listed when the reporter may choose one.
|
|
489
|
+
*/
|
|
490
|
+
export const getPublicIncidentForm: GetPublicIncidentFormFunction = (data: {
|
|
491
|
+
form: PublicIncidentFormSource;
|
|
492
|
+
askedDefinitions: Array<IncidentFormAskedDefinition>;
|
|
493
|
+
severities: Array<PublicIncidentFormSeverity>;
|
|
494
|
+
isCaptchaRequired: boolean;
|
|
495
|
+
}): PublicIncidentForm => {
|
|
496
|
+
const publicForm: PublicIncidentForm = {
|
|
497
|
+
name: typeof data.form.name === "string" ? data.form.name : "",
|
|
498
|
+
descriptionSetting: readDescriptionSetting(data.form.descriptionSetting),
|
|
499
|
+
isReporterDetailsRequired: data.form.isReporterDetailsRequired !== false,
|
|
500
|
+
customFields: getPublicIncidentFormFields(data.askedDefinitions),
|
|
501
|
+
isCaptchaRequired: data.isCaptchaRequired === true,
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
if (
|
|
505
|
+
typeof data.form.description === "string" &&
|
|
506
|
+
data.form.description.trim().length > 0
|
|
507
|
+
) {
|
|
508
|
+
publicForm.description = data.form.description;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (data.form.allowReporterToChooseSeverity === true) {
|
|
512
|
+
const severities: Array<PublicIncidentFormSeverity> = [];
|
|
513
|
+
|
|
514
|
+
// Only the three properties the page draws, whatever the caller passed.
|
|
515
|
+
for (const severity of data.severities || []) {
|
|
516
|
+
if (!severity || typeof severity._id !== "string" || !severity._id) {
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const listed: PublicIncidentFormSeverity = {
|
|
521
|
+
_id: severity._id,
|
|
522
|
+
name: typeof severity.name === "string" ? severity.name : "",
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
if (typeof severity.color === "string" && severity.color) {
|
|
526
|
+
listed.color = severity.color;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
severities.push(listed);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
publicForm.severities = severities;
|
|
533
|
+
|
|
534
|
+
const formSeverityId: string = data.form.incidentSeverityId
|
|
535
|
+
? data.form.incidentSeverityId.toString().toLowerCase()
|
|
536
|
+
: "";
|
|
537
|
+
|
|
538
|
+
const formSeverity: PublicIncidentFormSeverity | undefined =
|
|
539
|
+
severities.find((severity: PublicIncidentFormSeverity): boolean => {
|
|
540
|
+
return severity._id.toLowerCase() === formSeverityId;
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
if (formSeverity) {
|
|
544
|
+
publicForm.defaultIncidentSeverityId = formSeverity._id;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return publicForm;
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
type CleanAnswerFunction = (
|
|
552
|
+
field: PublicIncidentFormField,
|
|
553
|
+
value: unknown,
|
|
554
|
+
) => unknown;
|
|
555
|
+
|
|
556
|
+
/*
|
|
557
|
+
* An answer as it is checked and stored: text cleaned as its field shows
|
|
558
|
+
* it, "true" / "false" as the booleans the dashboard's own checkbox stores,
|
|
559
|
+
* and a multi-select as a list with each option once. Anything else is left
|
|
560
|
+
* for the checks to judge.
|
|
561
|
+
*/
|
|
562
|
+
const cleanAnswer: CleanAnswerFunction = (
|
|
563
|
+
field: PublicIncidentFormField,
|
|
564
|
+
value: unknown,
|
|
565
|
+
): unknown => {
|
|
566
|
+
if (typeof value === "string") {
|
|
567
|
+
switch (field.customFieldType) {
|
|
568
|
+
case CustomFieldType.Text:
|
|
569
|
+
return cleanSingleLine(value);
|
|
570
|
+
|
|
571
|
+
case CustomFieldType.LongText:
|
|
572
|
+
case CustomFieldType.Markdown:
|
|
573
|
+
return cleanMultiLine(value);
|
|
574
|
+
|
|
575
|
+
case CustomFieldType.Boolean: {
|
|
576
|
+
const text: string = cleanScalarText(value);
|
|
577
|
+
|
|
578
|
+
if (text === "true") {
|
|
579
|
+
return true;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if (text === "false") {
|
|
583
|
+
return false;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return text;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
case CustomFieldType.MultiSelectDropdown: {
|
|
590
|
+
const text: string = cleanScalarText(value);
|
|
591
|
+
|
|
592
|
+
return text.length > 0 ? [text] : [];
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
default:
|
|
596
|
+
return cleanScalarText(value);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if (
|
|
601
|
+
Array.isArray(value) &&
|
|
602
|
+
field.customFieldType === CustomFieldType.MultiSelectDropdown
|
|
603
|
+
) {
|
|
604
|
+
const entries: Array<unknown> = [];
|
|
605
|
+
const seen: Set<string> = new Set<string>();
|
|
606
|
+
|
|
607
|
+
for (const entry of value) {
|
|
608
|
+
const cleaned: unknown =
|
|
609
|
+
typeof entry === "string" ? cleanScalarText(entry) : entry;
|
|
610
|
+
|
|
611
|
+
if (cleaned === "") {
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const key: string = `${typeof cleaned}:${String(cleaned)}`;
|
|
616
|
+
|
|
617
|
+
if (seen.has(key)) {
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
seen.add(key);
|
|
622
|
+
entries.push(cleaned);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
return entries;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
return value;
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
type IsTooLongFunction = (value: unknown, maxLength: number) => boolean;
|
|
632
|
+
|
|
633
|
+
// A text answer, or any text entry of a list, longer than the cap.
|
|
634
|
+
const isTooLong: IsTooLongFunction = (
|
|
635
|
+
value: unknown,
|
|
636
|
+
maxLength: number,
|
|
637
|
+
): boolean => {
|
|
638
|
+
if (typeof value === "string") {
|
|
639
|
+
return value.length > maxLength;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (Array.isArray(value)) {
|
|
643
|
+
return value.some((entry: unknown): boolean => {
|
|
644
|
+
return typeof entry === "string" && entry.length > maxLength;
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
return false;
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
type ValidateAnswersFunction = (data: {
|
|
652
|
+
fields: Array<PublicIncidentFormField>;
|
|
653
|
+
answers: Record<string, unknown>;
|
|
654
|
+
errors: Array<string>;
|
|
655
|
+
}) => JSONObject;
|
|
656
|
+
|
|
657
|
+
type GetMaxChoicesFunction = (field: PublicIncidentFormField) => number;
|
|
658
|
+
|
|
659
|
+
// See INCIDENT_FORM_MULTI_SELECT_MAX_CHOICES.
|
|
660
|
+
const getMaxChoices: GetMaxChoicesFunction = (
|
|
661
|
+
field: PublicIncidentFormField,
|
|
662
|
+
): number => {
|
|
663
|
+
return Math.max(
|
|
664
|
+
INCIDENT_FORM_MULTI_SELECT_MAX_CHOICES,
|
|
665
|
+
parseCustomFieldDropdownOptions(field.dropdownOptions).length,
|
|
666
|
+
);
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
/*
|
|
670
|
+
* The custom field answers a submission may store. Only the fields the form
|
|
671
|
+
* asks for are read; an answer keyed by any other name is dropped unread.
|
|
672
|
+
*/
|
|
673
|
+
const validateAnswers: ValidateAnswersFunction = (data: {
|
|
674
|
+
fields: Array<PublicIncidentFormField>;
|
|
675
|
+
answers: Record<string, unknown>;
|
|
676
|
+
errors: Array<string>;
|
|
677
|
+
}): JSONObject => {
|
|
678
|
+
const accepted: JSONObject = {};
|
|
679
|
+
|
|
680
|
+
for (const field of data.fields) {
|
|
681
|
+
const answer: unknown = hasOwn(data.answers, field.name)
|
|
682
|
+
? data.answers[field.name]
|
|
683
|
+
: undefined;
|
|
684
|
+
|
|
685
|
+
/*
|
|
686
|
+
* An answer is one value - text, a number, a yes/no - or, for a
|
|
687
|
+
* multi-select, a list of them; nothing the form's page sends is an
|
|
688
|
+
* object. So an object is refused here, unread: checking it against
|
|
689
|
+
* its field (validateCustomFieldValues) would turn all of it into text
|
|
690
|
+
* twice before refusing it, and the request may carry millions of keys
|
|
691
|
+
* or a nesting millions deep, whose conversion alone blocks the server
|
|
692
|
+
* for seconds - a minute, at the body's 50 MB.
|
|
693
|
+
*/
|
|
694
|
+
if (
|
|
695
|
+
answer !== null &&
|
|
696
|
+
typeof answer === "object" &&
|
|
697
|
+
!Array.isArray(answer)
|
|
698
|
+
) {
|
|
699
|
+
data.errors.push(
|
|
700
|
+
fillMessage(
|
|
701
|
+
field.customFieldType === CustomFieldType.MultiSelectDropdown
|
|
702
|
+
? OBJECT_CHOICES_MESSAGE
|
|
703
|
+
: OBJECT_ANSWER_MESSAGE,
|
|
704
|
+
{ field: field.name },
|
|
705
|
+
),
|
|
706
|
+
);
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/*
|
|
711
|
+
* A list is bounded before anything reads its entries: cleaning,
|
|
712
|
+
* de-duplicating and checking them, and quoting the ones that are not
|
|
713
|
+
* options back in the refusal, all cost as much as the list is long,
|
|
714
|
+
* and the request may carry millions. Only a multi-select takes a list
|
|
715
|
+
* at all, and only as many entries as it could have choices.
|
|
716
|
+
*/
|
|
717
|
+
if (Array.isArray(answer) && answer.length > 0) {
|
|
718
|
+
if (field.customFieldType !== CustomFieldType.MultiSelectDropdown) {
|
|
719
|
+
data.errors.push(
|
|
720
|
+
fillMessage(NOT_A_LIST_MESSAGE, { field: field.name }),
|
|
721
|
+
);
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const maxChoices: number = getMaxChoices(field);
|
|
726
|
+
|
|
727
|
+
if (answer.length > maxChoices) {
|
|
728
|
+
data.errors.push(
|
|
729
|
+
fillMessage(TOO_MANY_CHOICES_MESSAGE, {
|
|
730
|
+
field: field.name,
|
|
731
|
+
maxLength: maxChoices,
|
|
732
|
+
}),
|
|
733
|
+
);
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/*
|
|
738
|
+
* Each choice is one option, so a list or an object inside the list
|
|
739
|
+
* is refused here, unread: turning a list nested thousands deep into
|
|
740
|
+
* text, as cleaning and checking the entries would, overflows the
|
|
741
|
+
* stack - a refusal must never become a thrown error.
|
|
742
|
+
*/
|
|
743
|
+
if (
|
|
744
|
+
answer.some((entry: unknown): boolean => {
|
|
745
|
+
return entry !== null && typeof entry === "object";
|
|
746
|
+
})
|
|
747
|
+
) {
|
|
748
|
+
data.errors.push(
|
|
749
|
+
fillMessage(NESTED_CHOICE_MESSAGE, { field: field.name }),
|
|
750
|
+
);
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
const value: unknown = cleanAnswer(field, answer);
|
|
756
|
+
|
|
757
|
+
if (isCustomFieldValueEmpty(value)) {
|
|
758
|
+
if (field.isRequired) {
|
|
759
|
+
data.errors.push(
|
|
760
|
+
fillMessage(
|
|
761
|
+
field.customFieldType === CustomFieldType.Boolean
|
|
762
|
+
? MUST_BE_CHECKED_MESSAGE
|
|
763
|
+
: REQUIRED_MESSAGE,
|
|
764
|
+
{ field: field.name },
|
|
765
|
+
),
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
if (isTooLong(value, INCIDENT_FORM_CUSTOM_FIELD_TEXT_MAX_LENGTH)) {
|
|
773
|
+
data.errors.push(
|
|
774
|
+
fillMessage(TOO_LONG_MESSAGE, {
|
|
775
|
+
field: field.name,
|
|
776
|
+
maxLength: INCIDENT_FORM_CUSTOM_FIELD_TEXT_MAX_LENGTH,
|
|
777
|
+
}),
|
|
778
|
+
);
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
const typeErrors: Array<CustomFieldValueValidationError> =
|
|
783
|
+
validateCustomFieldValues({
|
|
784
|
+
definitions: [
|
|
785
|
+
{
|
|
786
|
+
name: field.name,
|
|
787
|
+
customFieldType: field.customFieldType,
|
|
788
|
+
dropdownOptions: field.dropdownOptions,
|
|
789
|
+
},
|
|
790
|
+
],
|
|
791
|
+
customFields: { [field.name]: value },
|
|
792
|
+
storedCustomFields: {},
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
if (typeErrors.length > 0) {
|
|
796
|
+
data.errors.push(
|
|
797
|
+
...typeErrors.map((error: CustomFieldValueValidationError): string => {
|
|
798
|
+
return error.message;
|
|
799
|
+
}),
|
|
800
|
+
);
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/*
|
|
805
|
+
* A required yes/no question is an acknowledgement: only a tick answers
|
|
806
|
+
* it, as on the dashboard (CUSTOM_FIELD_MUST_BE_CHECKED_MESSAGE).
|
|
807
|
+
*/
|
|
808
|
+
if (
|
|
809
|
+
field.isRequired &&
|
|
810
|
+
field.customFieldType === CustomFieldType.Boolean &&
|
|
811
|
+
value !== true
|
|
812
|
+
) {
|
|
813
|
+
data.errors.push(
|
|
814
|
+
fillMessage(MUST_BE_CHECKED_MESSAGE, { field: field.name }),
|
|
815
|
+
);
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/*
|
|
820
|
+
* Defined, not assigned: a field may be named "__proto__", and assigning
|
|
821
|
+
* that name would set the object's prototype instead of storing the
|
|
822
|
+
* answer.
|
|
823
|
+
*/
|
|
824
|
+
Object.defineProperty(accepted, field.name, {
|
|
825
|
+
value: value as JSONValue,
|
|
826
|
+
enumerable: true,
|
|
827
|
+
writable: true,
|
|
828
|
+
configurable: true,
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
return accepted;
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
/*
|
|
836
|
+
* Email alone finds an address anywhere in a text - "Jane <jane@x.test>"
|
|
837
|
+
* passes it - so the whole answer must also be one ordinary address: the
|
|
838
|
+
* dot-atom half of Email's own pattern, anchored at both ends.
|
|
839
|
+
*/
|
|
840
|
+
export const WHOLE_EMAIL_ADDRESS: RegExp =
|
|
841
|
+
/^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
|
|
842
|
+
|
|
843
|
+
type ReadTextAnswerFunction = (data: {
|
|
844
|
+
value: unknown;
|
|
845
|
+
label: string;
|
|
846
|
+
clean: CleanTextFunction;
|
|
847
|
+
errors: Array<string>;
|
|
848
|
+
}) => string | null;
|
|
849
|
+
|
|
850
|
+
/*
|
|
851
|
+
* One of the built-in text questions: "" when it was not answered, the
|
|
852
|
+
* cleaned text when it was, and null - with the problem recorded - when the
|
|
853
|
+
* answer is not text at all, so the caller does not also call it missing.
|
|
854
|
+
*/
|
|
855
|
+
const readTextAnswer: ReadTextAnswerFunction = (data: {
|
|
856
|
+
value: unknown;
|
|
857
|
+
label: string;
|
|
858
|
+
clean: CleanTextFunction;
|
|
859
|
+
errors: Array<string>;
|
|
860
|
+
}): string | null => {
|
|
861
|
+
if (data.value === undefined || data.value === null) {
|
|
862
|
+
return "";
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
if (typeof data.value !== "string") {
|
|
866
|
+
data.errors.push(`${data.label} must be text.`);
|
|
867
|
+
return null;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
return data.clean(data.value);
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
type CheckLengthFunction = (data: {
|
|
874
|
+
text: string;
|
|
875
|
+
label: string;
|
|
876
|
+
isRequired: boolean;
|
|
877
|
+
maxLength: number;
|
|
878
|
+
errors: Array<string>;
|
|
879
|
+
}) => void;
|
|
880
|
+
|
|
881
|
+
// Required and too-long checks for a built-in text question.
|
|
882
|
+
const checkLength: CheckLengthFunction = (data: {
|
|
883
|
+
text: string;
|
|
884
|
+
label: string;
|
|
885
|
+
isRequired: boolean;
|
|
886
|
+
maxLength: number;
|
|
887
|
+
errors: Array<string>;
|
|
888
|
+
}): void => {
|
|
889
|
+
if (data.text.length === 0) {
|
|
890
|
+
if (data.isRequired) {
|
|
891
|
+
data.errors.push(fillMessage(REQUIRED_MESSAGE, { field: data.label }));
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if (data.text.length > data.maxLength) {
|
|
898
|
+
data.errors.push(
|
|
899
|
+
fillMessage(TOO_LONG_MESSAGE, {
|
|
900
|
+
field: data.label,
|
|
901
|
+
maxLength: data.maxLength,
|
|
902
|
+
}),
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
export type ValidateIncidentFormSubmissionFunction = (data: {
|
|
908
|
+
form: IncidentFormSubmissionRules;
|
|
909
|
+
// What getIncidentFormAskedDefinitions returns for the form.
|
|
910
|
+
askedDefinitions: Array<IncidentFormAskedDefinition>;
|
|
911
|
+
// The project's severities; only read when the reporter may choose one.
|
|
912
|
+
severities: Array<{ _id: string }>;
|
|
913
|
+
// The `data` of the request body, unread so far.
|
|
914
|
+
data: unknown;
|
|
915
|
+
}) => IncidentFormSubmissionValidationResult;
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
* Checks a submission against its form and returns either every problem
|
|
919
|
+
* with it, or the answers the incident may be built from. Never throws on a
|
|
920
|
+
* parsed JSON body, whatever it holds.
|
|
921
|
+
*
|
|
922
|
+
* - Title: always required, one line, at most 500 characters.
|
|
923
|
+
* - Description: ignored when the form hides it; required or optional
|
|
924
|
+
* otherwise, at most 20000 characters.
|
|
925
|
+
* - Severity: only read when the form lets the reporter choose, and then it
|
|
926
|
+
* must be one of the project's severities. Leaving it out means the
|
|
927
|
+
* form's own severity.
|
|
928
|
+
* - Name and email: required unless the form says otherwise; when given,
|
|
929
|
+
* the name is at most 100 characters and the email one valid address of
|
|
930
|
+
* at most 100.
|
|
931
|
+
* - Custom fields: only those the form asks, each checked as an API write
|
|
932
|
+
* would be, required ones filled in (a required yes/no ticked), text at
|
|
933
|
+
* most 10000 characters, never an object, a list only for a multi-select
|
|
934
|
+
* and then with at most 100 choices (or as many as it has options), none
|
|
935
|
+
* of them a list or an object. Answers to anything else are dropped.
|
|
936
|
+
*
|
|
937
|
+
* No check costs more for a larger body: text is measured, a list is
|
|
938
|
+
* counted and an object refused before anything reads what they hold.
|
|
939
|
+
*/
|
|
940
|
+
export const validateIncidentFormSubmission: ValidateIncidentFormSubmissionFunction =
|
|
941
|
+
(data: {
|
|
942
|
+
form: IncidentFormSubmissionRules;
|
|
943
|
+
askedDefinitions: Array<IncidentFormAskedDefinition>;
|
|
944
|
+
severities: Array<{ _id: string }>;
|
|
945
|
+
data: unknown;
|
|
946
|
+
}): IncidentFormSubmissionValidationResult => {
|
|
947
|
+
if (!isPlainObject(data.data)) {
|
|
948
|
+
return {
|
|
949
|
+
isValid: false,
|
|
950
|
+
errors: [
|
|
951
|
+
"The submission must be an object holding the form's answers.",
|
|
952
|
+
],
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
const input: Record<string, unknown> = data.data;
|
|
957
|
+
const form: IncidentFormSubmissionRules = data.form || {};
|
|
958
|
+
const labels: IncidentFormQuestionLabels = INCIDENT_FORM_QUESTION_LABELS;
|
|
959
|
+
const errors: Array<string> = [];
|
|
960
|
+
|
|
961
|
+
// Title: always asked, always required.
|
|
962
|
+
const title: string | null = readTextAnswer({
|
|
963
|
+
value: input["title"],
|
|
964
|
+
label: labels.title,
|
|
965
|
+
clean: cleanSingleLine,
|
|
966
|
+
errors: errors,
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
if (title !== null) {
|
|
970
|
+
checkLength({
|
|
971
|
+
text: title,
|
|
972
|
+
label: labels.title,
|
|
973
|
+
isRequired: true,
|
|
974
|
+
maxLength: INCIDENT_FORM_TITLE_MAX_LENGTH,
|
|
975
|
+
errors: errors,
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// Description: not even read when the form hides it.
|
|
980
|
+
const descriptionSetting: IncidentFormFieldSetting = readDescriptionSetting(
|
|
981
|
+
form.descriptionSetting,
|
|
982
|
+
);
|
|
983
|
+
|
|
984
|
+
let description: string | null = "";
|
|
985
|
+
|
|
986
|
+
if (descriptionSetting !== IncidentFormFieldSetting.Hidden) {
|
|
987
|
+
description = readTextAnswer({
|
|
988
|
+
value: input["description"],
|
|
989
|
+
label: labels.description,
|
|
990
|
+
clean: cleanMultiLine,
|
|
991
|
+
errors: errors,
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
if (description !== null) {
|
|
995
|
+
checkLength({
|
|
996
|
+
text: description,
|
|
997
|
+
label: labels.description,
|
|
998
|
+
isRequired: descriptionSetting === IncidentFormFieldSetting.Required,
|
|
999
|
+
maxLength: INCIDENT_FORM_DESCRIPTION_MAX_LENGTH,
|
|
1000
|
+
errors: errors,
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// Severity: only a choice the form offers, and only when it offers one.
|
|
1006
|
+
let incidentSeverityId: string | undefined = undefined;
|
|
1007
|
+
|
|
1008
|
+
if (form.allowReporterToChooseSeverity === true) {
|
|
1009
|
+
const requested: unknown = input["incidentSeverityId"];
|
|
1010
|
+
|
|
1011
|
+
const isUnanswered: boolean =
|
|
1012
|
+
requested === undefined ||
|
|
1013
|
+
requested === null ||
|
|
1014
|
+
(typeof requested === "string" && requested.trim().length === 0);
|
|
1015
|
+
|
|
1016
|
+
if (!isUnanswered) {
|
|
1017
|
+
const requestedId: string =
|
|
1018
|
+
typeof requested === "string" ? requested.trim().toLowerCase() : "";
|
|
1019
|
+
|
|
1020
|
+
const listed: { _id: string } | undefined = requestedId
|
|
1021
|
+
? (data.severities || []).find(
|
|
1022
|
+
(severity: { _id: string }): boolean => {
|
|
1023
|
+
return (
|
|
1024
|
+
typeof severity?._id === "string" &&
|
|
1025
|
+
severity._id.toLowerCase() === requestedId
|
|
1026
|
+
);
|
|
1027
|
+
},
|
|
1028
|
+
)
|
|
1029
|
+
: undefined;
|
|
1030
|
+
|
|
1031
|
+
if (listed) {
|
|
1032
|
+
incidentSeverityId = listed._id;
|
|
1033
|
+
} else {
|
|
1034
|
+
errors.push(
|
|
1035
|
+
`${labels.severity} must be one of the severities this form lists.`,
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// The reporter: required unless the form says otherwise.
|
|
1042
|
+
const detailsRequired: boolean = form.isReporterDetailsRequired !== false;
|
|
1043
|
+
|
|
1044
|
+
const reporterName: string | null = readTextAnswer({
|
|
1045
|
+
value: input["reporterName"],
|
|
1046
|
+
label: labels.reporterName,
|
|
1047
|
+
clean: cleanSingleLine,
|
|
1048
|
+
errors: errors,
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1051
|
+
if (reporterName !== null) {
|
|
1052
|
+
checkLength({
|
|
1053
|
+
text: reporterName,
|
|
1054
|
+
label: labels.reporterName,
|
|
1055
|
+
isRequired: detailsRequired,
|
|
1056
|
+
maxLength: INCIDENT_FORM_REPORTER_NAME_MAX_LENGTH,
|
|
1057
|
+
errors: errors,
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
let reporterEmail: string | null = readTextAnswer({
|
|
1062
|
+
value: input["reporterEmail"],
|
|
1063
|
+
label: labels.reporterEmail,
|
|
1064
|
+
clean: cleanScalarText,
|
|
1065
|
+
errors: errors,
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
if (reporterEmail !== null) {
|
|
1069
|
+
const errorCount: number = errors.length;
|
|
1070
|
+
|
|
1071
|
+
checkLength({
|
|
1072
|
+
text: reporterEmail,
|
|
1073
|
+
label: labels.reporterEmail,
|
|
1074
|
+
isRequired: detailsRequired,
|
|
1075
|
+
maxLength: INCIDENT_FORM_REPORTER_EMAIL_MAX_LENGTH,
|
|
1076
|
+
errors: errors,
|
|
1077
|
+
});
|
|
1078
|
+
|
|
1079
|
+
if (reporterEmail.length > 0 && errors.length === errorCount) {
|
|
1080
|
+
if (
|
|
1081
|
+
WHOLE_EMAIL_ADDRESS.test(reporterEmail) &&
|
|
1082
|
+
Email.isValid(reporterEmail)
|
|
1083
|
+
) {
|
|
1084
|
+
reporterEmail = new Email(reporterEmail).toString();
|
|
1085
|
+
} else {
|
|
1086
|
+
errors.push(`${labels.reporterEmail} is not a valid email address.`);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// Custom fields: only the ones the form asks for are read.
|
|
1092
|
+
const rawAnswers: unknown = input["customFields"];
|
|
1093
|
+
let answers: Record<string, unknown> = {};
|
|
1094
|
+
|
|
1095
|
+
if (isPlainObject(rawAnswers)) {
|
|
1096
|
+
answers = rawAnswers;
|
|
1097
|
+
} else if (rawAnswers !== undefined && rawAnswers !== null) {
|
|
1098
|
+
errors.push(
|
|
1099
|
+
"The custom field answers must be an object keyed by field name.",
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
const customFields: JSONObject = validateAnswers({
|
|
1104
|
+
fields: getPublicIncidentFormFields(data.askedDefinitions || []),
|
|
1105
|
+
answers: answers,
|
|
1106
|
+
errors: errors,
|
|
1107
|
+
});
|
|
1108
|
+
|
|
1109
|
+
if (errors.length > 0) {
|
|
1110
|
+
return { isValid: false, errors: errors };
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
const value: ValidatedIncidentFormSubmission = {
|
|
1114
|
+
title: title || "",
|
|
1115
|
+
customFields: customFields,
|
|
1116
|
+
};
|
|
1117
|
+
|
|
1118
|
+
if (description) {
|
|
1119
|
+
value.description = description;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
if (incidentSeverityId) {
|
|
1123
|
+
value.incidentSeverityId = incidentSeverityId;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
if (reporterName) {
|
|
1127
|
+
value.reporterName = reporterName;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
if (reporterEmail) {
|
|
1131
|
+
value.reporterEmail = reporterEmail;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
return { isValid: true, value: value };
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
export type FormatIncidentFormSubmissionErrorsFunction = (
|
|
1138
|
+
errors: Array<string>,
|
|
1139
|
+
) => string;
|
|
1140
|
+
|
|
1141
|
+
/** Every problem as one message, for an API error response. */
|
|
1142
|
+
export const formatIncidentFormSubmissionErrors: FormatIncidentFormSubmissionErrorsFunction =
|
|
1143
|
+
(errors: Array<string>): string => {
|
|
1144
|
+
return errors.join(" ");
|
|
1145
|
+
};
|