@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,1983 @@
|
|
|
1
|
+
import AllModelTypes, {
|
|
2
|
+
getModelTypeByName,
|
|
3
|
+
} from "../../../Models/DatabaseModels/Index";
|
|
4
|
+
import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
5
|
+
import RuleBaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/RuleBaseModel";
|
|
6
|
+
import Label from "../../../Models/DatabaseModels/Label";
|
|
7
|
+
import MessageQueue, {
|
|
8
|
+
MESSAGE_QUEUE_DISCOVERY_SOURCES,
|
|
9
|
+
getMessageQueueDiscoverySourceLabel,
|
|
10
|
+
} from "../../../Models/DatabaseModels/MessageQueue";
|
|
11
|
+
import MessageQueueLabelRule from "../../../Models/DatabaseModels/MessageQueueLabelRule";
|
|
12
|
+
import MessageQueueOwnerRule from "../../../Models/DatabaseModels/MessageQueueOwnerRule";
|
|
13
|
+
import MessageQueueOwnerTeam from "../../../Models/DatabaseModels/MessageQueueOwnerTeam";
|
|
14
|
+
import MessageQueueOwnerUser from "../../../Models/DatabaseModels/MessageQueueOwnerUser";
|
|
15
|
+
import Team from "../../../Models/DatabaseModels/Team";
|
|
16
|
+
import User from "../../../Models/DatabaseModels/User";
|
|
17
|
+
import DatabaseRequestType from "../../../Server/Types/BaseDatabase/DatabaseRequestType";
|
|
18
|
+
import ColumnPermissions from "../../../Server/Types/Database/Permissions/ColumnPermission";
|
|
19
|
+
import ModelPermission from "../../../Server/Types/Database/Permissions/Index";
|
|
20
|
+
import { ColumnAccessControl } from "../../../Types/BaseDatabase/AccessControl";
|
|
21
|
+
import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
22
|
+
import ColumnLength from "../../../Types/Database/ColumnLength";
|
|
23
|
+
import { TableColumnMetadata } from "../../../Types/Database/TableColumn";
|
|
24
|
+
import TableColumnType from "../../../Types/Database/TableColumnType";
|
|
25
|
+
import { getUniqueColumnsBy } from "../../../Types/Database/UniqueColumnBy";
|
|
26
|
+
import { UniqueColumnsTogetherMetadata } from "../../../Types/Database/UniqueColumnsTogether";
|
|
27
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
28
|
+
import IconProp from "../../../Types/Icon/IconProp";
|
|
29
|
+
import {
|
|
30
|
+
MESSAGE_QUEUE_DISPLAY_NAME_MAX_LENGTH,
|
|
31
|
+
MESSAGE_QUEUE_IDENTIFIER_MAX_LENGTH,
|
|
32
|
+
MessageQueueIdentity,
|
|
33
|
+
buildMessageQueueIdentifier,
|
|
34
|
+
parseMessageQueueIdentifier,
|
|
35
|
+
} from "../../../Types/MessageQueue/MessageQueueIdentity";
|
|
36
|
+
import { MESSAGE_QUEUE_DESTINATION_MAX_LENGTH } from "../../../Types/MessageQueue/MessagingTelemetryResolver";
|
|
37
|
+
import {
|
|
38
|
+
MESSAGING_SYSTEMS,
|
|
39
|
+
MessagingSystemDescriptor,
|
|
40
|
+
} from "../../../Types/MessageQueue/MessagingSystem";
|
|
41
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
42
|
+
import Permission, {
|
|
43
|
+
PermissionHelper,
|
|
44
|
+
PermissionProps,
|
|
45
|
+
UserTenantAccessPermission,
|
|
46
|
+
} from "../../../Types/Permission";
|
|
47
|
+
import { describe, expect, test } from "@jest/globals";
|
|
48
|
+
import { getMetadataArgsStorage } from "typeorm";
|
|
49
|
+
import { ColumnMetadataArgs } from "typeorm/metadata-args/ColumnMetadataArgs";
|
|
50
|
+
import { IndexMetadataArgs } from "typeorm/metadata-args/IndexMetadataArgs";
|
|
51
|
+
import { JoinColumnMetadataArgs } from "typeorm/metadata-args/JoinColumnMetadataArgs";
|
|
52
|
+
import { JoinTableMetadataArgs } from "typeorm/metadata-args/JoinTableMetadataArgs";
|
|
53
|
+
import { RelationMetadataArgs } from "typeorm/metadata-args/RelationMetadataArgs";
|
|
54
|
+
import fs from "fs";
|
|
55
|
+
import path from "path";
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
* The Queues product is persisted as five TypeORM entities: the MessageQueue
|
|
59
|
+
* root and the owner / rule models cloned from the Databases product.
|
|
60
|
+
*
|
|
61
|
+
* What is pinned here only fails in production:
|
|
62
|
+
*
|
|
63
|
+
* - registration in Models/Index.ts (no table, no API otherwise)
|
|
64
|
+
* - the table / route / display names other layers key on, and staying
|
|
65
|
+
* clear of the BullMQ `Queue` the workers already import
|
|
66
|
+
* - the identity index discovery relies on for race safety, and the two
|
|
67
|
+
* NAMED partial unique indexes a generated migration would otherwise
|
|
68
|
+
* drop
|
|
69
|
+
* - the column ACL contract: DatabaseService.create runs onBeforeCreate
|
|
70
|
+
* BEFORE the column permission check, so every column the service sets on
|
|
71
|
+
* a manual create must be user-creatable, and every column only discovery
|
|
72
|
+
* writes must be closed to users - checked here against the real
|
|
73
|
+
* ColumnPermission / CreatePermission code, not just the declarations
|
|
74
|
+
* - the rule criteria fields and the permissions
|
|
75
|
+
* - Postgres's 63-character identifier limit
|
|
76
|
+
*
|
|
77
|
+
* Pure metadata and in-process permission checks - no Postgres connection.
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
type ModelType = { new (): BaseModel };
|
|
81
|
+
|
|
82
|
+
const MODEL_TYPES: Array<ModelType> = AllModelTypes as Array<ModelType>;
|
|
83
|
+
|
|
84
|
+
interface QueueModelSpec {
|
|
85
|
+
name: string;
|
|
86
|
+
modelType: ModelType;
|
|
87
|
+
tableName: string;
|
|
88
|
+
crudApiPath: string;
|
|
89
|
+
singularName: string;
|
|
90
|
+
pluralName: string;
|
|
91
|
+
icon: IconProp;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const QUEUE_MODELS: Array<QueueModelSpec> = [
|
|
95
|
+
{
|
|
96
|
+
name: "MessageQueue",
|
|
97
|
+
modelType: MessageQueue,
|
|
98
|
+
tableName: "MessageQueue",
|
|
99
|
+
crudApiPath: "/message-queue",
|
|
100
|
+
singularName: "Queue",
|
|
101
|
+
pluralName: "Queues",
|
|
102
|
+
icon: IconProp.QueueList,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "MessageQueueOwnerTeam",
|
|
106
|
+
modelType: MessageQueueOwnerTeam,
|
|
107
|
+
tableName: "MessageQueueOwnerTeam",
|
|
108
|
+
crudApiPath: "/message-queue-owner-team",
|
|
109
|
+
singularName: "Queue Team Owner",
|
|
110
|
+
pluralName: "Queue Team Owners",
|
|
111
|
+
icon: IconProp.Cube,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "MessageQueueOwnerUser",
|
|
115
|
+
modelType: MessageQueueOwnerUser,
|
|
116
|
+
tableName: "MessageQueueOwnerUser",
|
|
117
|
+
crudApiPath: "/message-queue-owner-user",
|
|
118
|
+
singularName: "Queue User Owner",
|
|
119
|
+
pluralName: "Queue User Owners",
|
|
120
|
+
icon: IconProp.Cube,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "MessageQueueLabelRule",
|
|
124
|
+
modelType: MessageQueueLabelRule,
|
|
125
|
+
tableName: "MessageQueueLabelRule",
|
|
126
|
+
crudApiPath: "/message-queue-label-rule",
|
|
127
|
+
singularName: "Queue Label Rule",
|
|
128
|
+
pluralName: "Queue Label Rules",
|
|
129
|
+
icon: IconProp.Tag,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "MessageQueueOwnerRule",
|
|
133
|
+
modelType: MessageQueueOwnerRule,
|
|
134
|
+
tableName: "MessageQueueOwnerRule",
|
|
135
|
+
crudApiPath: "/message-queue-owner-rule",
|
|
136
|
+
singularName: "Queue Owner Rule",
|
|
137
|
+
pluralName: "Queue Owner Rules",
|
|
138
|
+
// Every infrastructure owner rule (Ceph, VMware, Cloud, Databases) uses User.
|
|
139
|
+
icon: IconProp.User,
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
// Every child of MessageQueue and the relation property it reaches it by.
|
|
144
|
+
const MESSAGE_QUEUE_CHILDREN: Array<[ModelType, string]> = [
|
|
145
|
+
[MessageQueueOwnerTeam, "messageQueue"],
|
|
146
|
+
[MessageQueueOwnerUser, "messageQueue"],
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
const MESSAGE_QUEUE_PERMISSIONS: Array<string> = [
|
|
150
|
+
"CreateMessageQueue",
|
|
151
|
+
"DeleteMessageQueue",
|
|
152
|
+
"EditMessageQueue",
|
|
153
|
+
"ReadMessageQueue",
|
|
154
|
+
"CreateMessageQueueOwnerTeam",
|
|
155
|
+
"DeleteMessageQueueOwnerTeam",
|
|
156
|
+
"EditMessageQueueOwnerTeam",
|
|
157
|
+
"ReadMessageQueueOwnerTeam",
|
|
158
|
+
"CreateMessageQueueOwnerUser",
|
|
159
|
+
"DeleteMessageQueueOwnerUser",
|
|
160
|
+
"EditMessageQueueOwnerUser",
|
|
161
|
+
"ReadMessageQueueOwnerUser",
|
|
162
|
+
"CreateMessageQueueLabelRule",
|
|
163
|
+
"DeleteMessageQueueLabelRule",
|
|
164
|
+
"EditMessageQueueLabelRule",
|
|
165
|
+
"ReadMessageQueueLabelRule",
|
|
166
|
+
"CreateMessageQueueOwnerRule",
|
|
167
|
+
"DeleteMessageQueueOwnerRule",
|
|
168
|
+
"EditMessageQueueOwnerRule",
|
|
169
|
+
"ReadMessageQueueOwnerRule",
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
// The rule criteria fields the registry, the engines and the forms share.
|
|
173
|
+
const RULE_CRITERIA_FIELDS: Array<string> = [
|
|
174
|
+
"messageQueueLabels",
|
|
175
|
+
"messageQueueNamePattern",
|
|
176
|
+
"messageQueueDescriptionPattern",
|
|
177
|
+
"messageQueueSystemPattern",
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
/*
|
|
181
|
+
* Every column MessageQueueService.onBeforeCreate may set on a manual
|
|
182
|
+
* (non-root) create, plus what DatabaseService itself stamps (projectId,
|
|
183
|
+
* createdByUserId) and what the create form may send.
|
|
184
|
+
*/
|
|
185
|
+
const MESSAGE_QUEUE_USER_CREATE_COLUMNS: Array<string> = [
|
|
186
|
+
"project",
|
|
187
|
+
"projectId",
|
|
188
|
+
"name",
|
|
189
|
+
"description",
|
|
190
|
+
"queueIdentifier",
|
|
191
|
+
"messagingSystem",
|
|
192
|
+
"destinationName",
|
|
193
|
+
"brokerScope",
|
|
194
|
+
"discoverySource",
|
|
195
|
+
"labels",
|
|
196
|
+
"isArchived",
|
|
197
|
+
"createdByUser",
|
|
198
|
+
"createdByUserId",
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
// Columns a person may change after creation.
|
|
202
|
+
const MESSAGE_QUEUE_USER_UPDATE_COLUMNS: Array<string> = [
|
|
203
|
+
"name",
|
|
204
|
+
"description",
|
|
205
|
+
"labels",
|
|
206
|
+
"isArchived",
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
// Columns only discovery, the sighting heartbeat and the archive paths write, as root.
|
|
210
|
+
const MESSAGE_QUEUE_ROOT_ONLY_COLUMNS: Array<string> = [
|
|
211
|
+
"slug",
|
|
212
|
+
"brokerAddress",
|
|
213
|
+
"lastSeenAt",
|
|
214
|
+
"brokerMetricsLastSeenAt",
|
|
215
|
+
"autoArchivedAt",
|
|
216
|
+
"manuallyRestoredAt",
|
|
217
|
+
"automaticAssignments",
|
|
218
|
+
"archivedAt",
|
|
219
|
+
"archivedByUser",
|
|
220
|
+
"archivedByUserId",
|
|
221
|
+
"deletedByUser",
|
|
222
|
+
"deletedByUserId",
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
const PERMISSION_PROPS: Array<PermissionProps> =
|
|
226
|
+
PermissionHelper.getAllPermissionProps();
|
|
227
|
+
|
|
228
|
+
const PERMISSION_PROPS_BY_NAME: Map<string, PermissionProps> = new Map(
|
|
229
|
+
PERMISSION_PROPS.map((props: PermissionProps) => {
|
|
230
|
+
return [props.permission.toString(), props];
|
|
231
|
+
}),
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
235
|
+
"9e1b6b0e-0000-4000-8000-00000000c7a1",
|
|
236
|
+
);
|
|
237
|
+
const USER_ID: ObjectID = new ObjectID("9e1b6b0e-0000-4000-8000-00000000c7a2");
|
|
238
|
+
|
|
239
|
+
/*
|
|
240
|
+
* DatabaseCommonInteractionPropsUtil.getUserPermissions only reads the tenant
|
|
241
|
+
* bucket when props.tenantId is set, and drops every permission whose
|
|
242
|
+
* isBlockPermission does not match. Getting either wrong yields an empty
|
|
243
|
+
* permission set, which would make every "does not throw" below pass for the
|
|
244
|
+
* wrong reason - hence the harness guard test further down.
|
|
245
|
+
*/
|
|
246
|
+
function makeProps(
|
|
247
|
+
permissions: Array<Permission>,
|
|
248
|
+
): DatabaseCommonInteractionProps {
|
|
249
|
+
const tenantPermission: UserTenantAccessPermission = {
|
|
250
|
+
projectId: PROJECT_ID,
|
|
251
|
+
_type: "UserTenantAccessPermission",
|
|
252
|
+
permissions: permissions.map((permission: Permission) => {
|
|
253
|
+
return {
|
|
254
|
+
_type: "UserPermission",
|
|
255
|
+
permission: permission,
|
|
256
|
+
labelIds: [],
|
|
257
|
+
isBlockPermission: false,
|
|
258
|
+
};
|
|
259
|
+
}),
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
userId: USER_ID,
|
|
264
|
+
tenantId: PROJECT_ID,
|
|
265
|
+
userTenantAccessPermission: {
|
|
266
|
+
[PROJECT_ID.toString()]: tenantPermission,
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// What a manual create posts after MessageQueueService.onBeforeCreate ran.
|
|
272
|
+
function manualCreateData(): MessageQueue {
|
|
273
|
+
const data: MessageQueue = new MessageQueue();
|
|
274
|
+
data.projectId = PROJECT_ID;
|
|
275
|
+
data.name = "orders";
|
|
276
|
+
data.description = "Orders from checkout to fulfilment";
|
|
277
|
+
data.messagingSystem = "servicebus";
|
|
278
|
+
data.destinationName = "orders";
|
|
279
|
+
data.brokerScope = "orders-prod";
|
|
280
|
+
data.queueIdentifier = "servicebus|orders-prod|orders";
|
|
281
|
+
data.discoverySource = "manual";
|
|
282
|
+
data.createdByUserId = USER_ID;
|
|
283
|
+
return data;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
type CheckFunction = () => void;
|
|
287
|
+
|
|
288
|
+
function checkCreate(
|
|
289
|
+
modelType: ModelType,
|
|
290
|
+
data: BaseModel,
|
|
291
|
+
permissions: Array<Permission>,
|
|
292
|
+
): CheckFunction {
|
|
293
|
+
return () => {
|
|
294
|
+
ModelPermission.checkCreatePermissions(
|
|
295
|
+
modelType,
|
|
296
|
+
data,
|
|
297
|
+
makeProps(permissions),
|
|
298
|
+
);
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function checkColumns(
|
|
303
|
+
modelType: ModelType,
|
|
304
|
+
data: BaseModel,
|
|
305
|
+
permissions: Array<Permission>,
|
|
306
|
+
requestType: DatabaseRequestType,
|
|
307
|
+
): CheckFunction {
|
|
308
|
+
return () => {
|
|
309
|
+
ColumnPermissions.checkDataColumnPermissions(
|
|
310
|
+
modelType,
|
|
311
|
+
data,
|
|
312
|
+
makeProps(permissions),
|
|
313
|
+
requestType,
|
|
314
|
+
);
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function allReferencedPermissions(model: BaseModel): Array<Permission> {
|
|
319
|
+
const permissions: Array<Permission> = [
|
|
320
|
+
...(model.createRecordPermissions || []),
|
|
321
|
+
...(model.readRecordPermissions || []),
|
|
322
|
+
...(model.updateRecordPermissions || []),
|
|
323
|
+
...(model.deleteRecordPermissions || []),
|
|
324
|
+
];
|
|
325
|
+
|
|
326
|
+
for (const column of model.getTableColumns().columns) {
|
|
327
|
+
const accessControl: ColumnAccessControl | undefined =
|
|
328
|
+
model.getColumnAccessControlFor(column) || undefined;
|
|
329
|
+
|
|
330
|
+
if (!accessControl) {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
permissions.push(
|
|
335
|
+
...(accessControl.create || []),
|
|
336
|
+
...(accessControl.read || []),
|
|
337
|
+
...(accessControl.update || []),
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return permissions;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/*
|
|
345
|
+
* DatabaseBaseModel's own columns (_id, timestamps, version). They carry the
|
|
346
|
+
* table ACL by default (or none, for version) and are never written through
|
|
347
|
+
* the column check, so the per-column sweeps below skip them.
|
|
348
|
+
*/
|
|
349
|
+
const BASE_COLUMNS: Array<string> = [
|
|
350
|
+
"_id",
|
|
351
|
+
"createdAt",
|
|
352
|
+
"updatedAt",
|
|
353
|
+
"deletedAt",
|
|
354
|
+
"version",
|
|
355
|
+
];
|
|
356
|
+
|
|
357
|
+
function ownColumns(model: BaseModel): Array<string> {
|
|
358
|
+
return model.getTableColumns().columns.filter((column: string): boolean => {
|
|
359
|
+
return !BASE_COLUMNS.includes(column);
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function columnAccess(model: BaseModel, column: string): ColumnAccessControl {
|
|
364
|
+
const accessControl: ColumnAccessControl | null =
|
|
365
|
+
model.getColumnAccessControlFor(column);
|
|
366
|
+
|
|
367
|
+
if (!accessControl) {
|
|
368
|
+
throw new Error(`${column} has no @ColumnAccessControl`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return accessControl;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function columnArgs(
|
|
375
|
+
modelType: ModelType,
|
|
376
|
+
property: string,
|
|
377
|
+
): ColumnMetadataArgs {
|
|
378
|
+
const column: ColumnMetadataArgs | undefined = (
|
|
379
|
+
getMetadataArgsStorage().columns as Array<ColumnMetadataArgs>
|
|
380
|
+
).find((candidate: ColumnMetadataArgs) => {
|
|
381
|
+
return (
|
|
382
|
+
candidate.target === modelType && candidate.propertyName === property
|
|
383
|
+
);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
if (!column) {
|
|
387
|
+
throw new Error(`${modelType.name}.${property} is not a registered column`);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return column;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function relationArgs(
|
|
394
|
+
modelType: ModelType,
|
|
395
|
+
property: string,
|
|
396
|
+
): RelationMetadataArgs {
|
|
397
|
+
const relation: RelationMetadataArgs | undefined = (
|
|
398
|
+
getMetadataArgsStorage().relations as Array<RelationMetadataArgs>
|
|
399
|
+
).find((candidate: RelationMetadataArgs) => {
|
|
400
|
+
return (
|
|
401
|
+
candidate.target === modelType && candidate.propertyName === property
|
|
402
|
+
);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
if (!relation) {
|
|
406
|
+
throw new Error(
|
|
407
|
+
`${modelType.name}.${property} is not a registered relation`,
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return relation;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function joinColumnArgs(
|
|
415
|
+
modelType: ModelType,
|
|
416
|
+
property: string,
|
|
417
|
+
): JoinColumnMetadataArgs {
|
|
418
|
+
const joinColumn: JoinColumnMetadataArgs | undefined = (
|
|
419
|
+
getMetadataArgsStorage().joinColumns as Array<JoinColumnMetadataArgs>
|
|
420
|
+
).find((candidate: JoinColumnMetadataArgs) => {
|
|
421
|
+
return (
|
|
422
|
+
candidate.target === modelType && candidate.propertyName === property
|
|
423
|
+
);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
if (!joinColumn) {
|
|
427
|
+
throw new Error(`${modelType.name}.${property} has no @JoinColumn`);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return joinColumn;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function joinTableArgs(
|
|
434
|
+
modelType: ModelType,
|
|
435
|
+
property: string,
|
|
436
|
+
): JoinTableMetadataArgs {
|
|
437
|
+
const joinTable: JoinTableMetadataArgs | undefined = (
|
|
438
|
+
getMetadataArgsStorage().joinTables as Array<JoinTableMetadataArgs>
|
|
439
|
+
).find((candidate: JoinTableMetadataArgs) => {
|
|
440
|
+
return (
|
|
441
|
+
candidate.target === modelType && candidate.propertyName === property
|
|
442
|
+
);
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
if (!joinTable) {
|
|
446
|
+
throw new Error(`${modelType.name}.${property} has no @JoinTable`);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return joinTable;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function classIndexes(modelType: ModelType): Array<IndexMetadataArgs> {
|
|
453
|
+
return getMetadataArgsStorage().indices.filter(
|
|
454
|
+
(index: IndexMetadataArgs): boolean => {
|
|
455
|
+
return index.target === modelType;
|
|
456
|
+
},
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function indexOnColumns(
|
|
461
|
+
modelType: ModelType,
|
|
462
|
+
columns: Array<string>,
|
|
463
|
+
): IndexMetadataArgs | undefined {
|
|
464
|
+
return classIndexes(modelType).find((index: IndexMetadataArgs): boolean => {
|
|
465
|
+
return (
|
|
466
|
+
Array.isArray(index.columns) &&
|
|
467
|
+
index.columns.length === columns.length &&
|
|
468
|
+
columns.every((column: string): boolean => {
|
|
469
|
+
return (index.columns as Array<string>).includes(column);
|
|
470
|
+
})
|
|
471
|
+
);
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function propertyIndex(
|
|
476
|
+
modelType: ModelType,
|
|
477
|
+
property: string,
|
|
478
|
+
): IndexMetadataArgs | undefined {
|
|
479
|
+
// A property-level @Index() is recorded with [propertyName] as its columns.
|
|
480
|
+
return classIndexes(modelType).find((index: IndexMetadataArgs): boolean => {
|
|
481
|
+
return (
|
|
482
|
+
Array.isArray(index.columns) &&
|
|
483
|
+
index.columns.length === 1 &&
|
|
484
|
+
index.columns[0] === property &&
|
|
485
|
+
!index.name
|
|
486
|
+
);
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function namedIndex(
|
|
491
|
+
modelType: ModelType,
|
|
492
|
+
name: string,
|
|
493
|
+
): IndexMetadataArgs | undefined {
|
|
494
|
+
return classIndexes(modelType).find((index: IndexMetadataArgs): boolean => {
|
|
495
|
+
return index.name === name;
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function modelSource(modelName: string): string {
|
|
500
|
+
return fs.readFileSync(
|
|
501
|
+
path.join(
|
|
502
|
+
__dirname,
|
|
503
|
+
"..",
|
|
504
|
+
"..",
|
|
505
|
+
"..",
|
|
506
|
+
"Models",
|
|
507
|
+
"DatabaseModels",
|
|
508
|
+
`${modelName}.ts`,
|
|
509
|
+
),
|
|
510
|
+
"utf8",
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/*
|
|
515
|
+
* The source with its comments removed. Comments may name the template a
|
|
516
|
+
* model was cloned from ("like DatabaseServer.databaseIdentifier"); code may
|
|
517
|
+
* not. A line comment must start a line or follow whitespace, so the "//" of
|
|
518
|
+
* a URL inside a string is left alone.
|
|
519
|
+
*/
|
|
520
|
+
function codeOf(source: string): string {
|
|
521
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|\s)\/\/.*$/gm, "");
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function sorted(values: Array<Permission | string>): Array<string> {
|
|
525
|
+
return values
|
|
526
|
+
.map((value: Permission | string): string => {
|
|
527
|
+
return value.toString();
|
|
528
|
+
})
|
|
529
|
+
.sort();
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
describe("Queues (MessageQueue) models", () => {
|
|
533
|
+
test("the inventory is complete", () => {
|
|
534
|
+
expect(QUEUE_MODELS.length).toBe(5);
|
|
535
|
+
expect(MESSAGE_QUEUE_PERMISSIONS.length).toBe(20);
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
describe("harness guard", () => {
|
|
539
|
+
/*
|
|
540
|
+
* Runs first on purpose. Most permission assertions below are "does not
|
|
541
|
+
* throw", which a props object the permission util silently ignored
|
|
542
|
+
* would also satisfy. A caller holding only a READ grant must be refused
|
|
543
|
+
* a create; if it is not, makeProps is broken, not the model.
|
|
544
|
+
*/
|
|
545
|
+
test("a read-only caller is refused a manual create", () => {
|
|
546
|
+
expect(
|
|
547
|
+
checkCreate(MessageQueue, manualCreateData(), [
|
|
548
|
+
Permission.ReadMessageQueue,
|
|
549
|
+
]),
|
|
550
|
+
).toThrow();
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
test("a caller with no permissions at all is refused a column write", () => {
|
|
554
|
+
expect(
|
|
555
|
+
checkColumns(
|
|
556
|
+
MessageQueue,
|
|
557
|
+
manualCreateData(),
|
|
558
|
+
[],
|
|
559
|
+
DatabaseRequestType.Create,
|
|
560
|
+
),
|
|
561
|
+
).toThrow(BadDataException);
|
|
562
|
+
});
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
describe("registration and identity", () => {
|
|
566
|
+
test.each(QUEUE_MODELS)(
|
|
567
|
+
"$name is registered in Models/Index.ts",
|
|
568
|
+
(spec: QueueModelSpec) => {
|
|
569
|
+
/*
|
|
570
|
+
* Boot-time createTables() and the migration generator both iterate
|
|
571
|
+
* that array. A model missing from it type-checks, imports fine, and
|
|
572
|
+
* simply has no table in Postgres.
|
|
573
|
+
*/
|
|
574
|
+
expect(MODEL_TYPES).toContain(spec.modelType);
|
|
575
|
+
expect(
|
|
576
|
+
MODEL_TYPES.filter((modelType: ModelType): boolean => {
|
|
577
|
+
return modelType === spec.modelType;
|
|
578
|
+
}),
|
|
579
|
+
).toHaveLength(1);
|
|
580
|
+
},
|
|
581
|
+
);
|
|
582
|
+
|
|
583
|
+
test.each(QUEUE_MODELS)(
|
|
584
|
+
"$name resolves by its table name through getModelTypeByName",
|
|
585
|
+
(spec: QueueModelSpec) => {
|
|
586
|
+
// The generic API / workflow layers look models up by table name.
|
|
587
|
+
expect(getModelTypeByName(spec.tableName)).toBe(spec.modelType);
|
|
588
|
+
},
|
|
589
|
+
);
|
|
590
|
+
|
|
591
|
+
test.each(QUEUE_MODELS)(
|
|
592
|
+
"$name carries its own table, route, display names and icon",
|
|
593
|
+
(spec: QueueModelSpec) => {
|
|
594
|
+
const model: BaseModel = new spec.modelType();
|
|
595
|
+
|
|
596
|
+
expect(spec.modelType.name).toBe(spec.name);
|
|
597
|
+
expect(model.tableName).toBe(spec.tableName);
|
|
598
|
+
expect(model.getCrudApiPath()?.toString()).toBe(spec.crudApiPath);
|
|
599
|
+
expect(model.singularName).toBe(spec.singularName);
|
|
600
|
+
expect(model.pluralName).toBe(spec.pluralName);
|
|
601
|
+
expect(model.icon).toBe(spec.icon);
|
|
602
|
+
expect(model.getTenantColumn()).toBe("projectId");
|
|
603
|
+
expect(model.enableDocumentation).toBe(true);
|
|
604
|
+
expect((model.tableDescription || "").length).toBeGreaterThan(20);
|
|
605
|
+
},
|
|
606
|
+
);
|
|
607
|
+
|
|
608
|
+
test("no Queues route or table name collides with another model", () => {
|
|
609
|
+
const routes: Map<string, number> = new Map<string, number>();
|
|
610
|
+
const tables: Map<string, number> = new Map<string, number>();
|
|
611
|
+
|
|
612
|
+
for (const modelType of MODEL_TYPES) {
|
|
613
|
+
const model: BaseModel = new modelType();
|
|
614
|
+
const route: string | undefined = model.getCrudApiPath()?.toString();
|
|
615
|
+
|
|
616
|
+
if (route) {
|
|
617
|
+
routes.set(route, (routes.get(route) || 0) + 1);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (model.tableName) {
|
|
621
|
+
tables.set(model.tableName, (tables.get(model.tableName) || 0) + 1);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
for (const spec of QUEUE_MODELS) {
|
|
626
|
+
expect(routes.get(spec.crudApiPath)).toBe(1);
|
|
627
|
+
expect(tables.get(spec.tableName)).toBe(1);
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
test("does not take the name the BullMQ job queue already uses", () => {
|
|
632
|
+
/*
|
|
633
|
+
* Server/Infrastructure/Queue.ts default-exports `Queue` (BullMQ) and
|
|
634
|
+
* every worker imports it. No model may claim the bare name, and every
|
|
635
|
+
* Queues route lives under /message-queue.
|
|
636
|
+
*/
|
|
637
|
+
expect(getModelTypeByName("Queue")).toBeNull();
|
|
638
|
+
|
|
639
|
+
for (const spec of QUEUE_MODELS) {
|
|
640
|
+
expect(spec.tableName).not.toBe("Queue");
|
|
641
|
+
expect(spec.crudApiPath.startsWith("/message-queue")).toBe(true);
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
test.each(QUEUE_MODELS)(
|
|
646
|
+
"$name source carries no Databases leftovers",
|
|
647
|
+
(spec: QueueModelSpec) => {
|
|
648
|
+
/*
|
|
649
|
+
* The models were cloned from the Databases product. A leftover
|
|
650
|
+
* `/database-server` route or `databaseServerId` column reads fine in
|
|
651
|
+
* review and silently binds the wrong table. Comments are exempt:
|
|
652
|
+
* pointing at the template a decision was copied from is
|
|
653
|
+
* documentation, not a binding.
|
|
654
|
+
*/
|
|
655
|
+
const source: string = codeOf(modelSource(spec.name));
|
|
656
|
+
|
|
657
|
+
// Guards the stripping itself: the class must still be in there.
|
|
658
|
+
expect(source).toContain(`export default class ${spec.name}`);
|
|
659
|
+
|
|
660
|
+
for (const token of [
|
|
661
|
+
"DatabaseServer",
|
|
662
|
+
"databaseServer",
|
|
663
|
+
"database-server",
|
|
664
|
+
"dbSystem",
|
|
665
|
+
"databases",
|
|
666
|
+
"Ceph",
|
|
667
|
+
"ceph",
|
|
668
|
+
]) {
|
|
669
|
+
expect({ token, found: source.includes(token) }).toEqual({
|
|
670
|
+
token,
|
|
671
|
+
found: false,
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
},
|
|
675
|
+
);
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
describe("MessageQueue", () => {
|
|
679
|
+
const model: MessageQueue = new MessageQueue();
|
|
680
|
+
|
|
681
|
+
test("is unique per project on queueIdentifier - named, partial, the discovery join key", () => {
|
|
682
|
+
/*
|
|
683
|
+
* Discovery find-or-creates by queueIdentifier, and worker runs race on
|
|
684
|
+
* the same queue; only a DB-level unique index collapses them into one
|
|
685
|
+
* row. Partial (live rows only) and declared by name, so the drift
|
|
686
|
+
* check keeps it.
|
|
687
|
+
*/
|
|
688
|
+
const found: IndexMetadataArgs | undefined = namedIndex(
|
|
689
|
+
MessageQueue,
|
|
690
|
+
"IDX_message_queue_identifier",
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
expect(found).toBeDefined();
|
|
694
|
+
expect(found?.unique).toBe(true);
|
|
695
|
+
expect(found?.columns).toEqual(["projectId", "queueIdentifier"]);
|
|
696
|
+
expect(found?.where).toBe('"deletedAt" IS NULL');
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
test("the display name is NOT unique - two brokers may each have an 'orders'", () => {
|
|
700
|
+
const nameIndexes: Array<IndexMetadataArgs> = classIndexes(
|
|
701
|
+
MessageQueue,
|
|
702
|
+
).filter((index: IndexMetadataArgs): boolean => {
|
|
703
|
+
return (
|
|
704
|
+
Array.isArray(index.columns) &&
|
|
705
|
+
(index.columns as Array<string>).includes("name")
|
|
706
|
+
);
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
expect(nameIndexes.length).toBeGreaterThan(0);
|
|
710
|
+
|
|
711
|
+
for (const index of nameIndexes) {
|
|
712
|
+
expect(index.unique).not.toBe(true);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// Nor the app-level @UniqueColumnBy check ServerlessFunction.name carries.
|
|
716
|
+
expect(Object.keys(getUniqueColumnsBy(new MessageQueue()))).toEqual([]);
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
test("declares IDX_message_queue_slug by name, UNIQUE and partial", () => {
|
|
720
|
+
/*
|
|
721
|
+
* TypeORM's schema builder matches database indexes to entity
|
|
722
|
+
* metadata by name and drops every one it cannot find, so the partial
|
|
723
|
+
* slug index must be declared natively and named.
|
|
724
|
+
*/
|
|
725
|
+
const found: IndexMetadataArgs | undefined = namedIndex(
|
|
726
|
+
MessageQueue,
|
|
727
|
+
"IDX_message_queue_slug",
|
|
728
|
+
);
|
|
729
|
+
|
|
730
|
+
expect(found).toBeDefined();
|
|
731
|
+
expect(found?.unique).toBe(true);
|
|
732
|
+
expect(found?.columns).toEqual(["slug"]);
|
|
733
|
+
expect(found?.where).toBe('"deletedAt" IS NULL');
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
test("names exactly two indexes - the partial unique ones", () => {
|
|
737
|
+
const named: Array<string> = classIndexes(MessageQueue)
|
|
738
|
+
.map((index: IndexMetadataArgs): string => {
|
|
739
|
+
return index.name || "";
|
|
740
|
+
})
|
|
741
|
+
.filter((name: string): boolean => {
|
|
742
|
+
return name.length > 0;
|
|
743
|
+
})
|
|
744
|
+
.sort();
|
|
745
|
+
|
|
746
|
+
expect(named).toEqual([
|
|
747
|
+
"IDX_message_queue_identifier",
|
|
748
|
+
"IDX_message_queue_slug",
|
|
749
|
+
]);
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
test("indexes the list-page filters: (projectId, isArchived) and (projectId, messagingSystem)", () => {
|
|
753
|
+
const archived: IndexMetadataArgs | undefined = indexOnColumns(
|
|
754
|
+
MessageQueue,
|
|
755
|
+
["projectId", "isArchived"],
|
|
756
|
+
);
|
|
757
|
+
const system: IndexMetadataArgs | undefined = indexOnColumns(
|
|
758
|
+
MessageQueue,
|
|
759
|
+
["projectId", "messagingSystem"],
|
|
760
|
+
);
|
|
761
|
+
|
|
762
|
+
expect(archived).toBeDefined();
|
|
763
|
+
expect(archived?.unique).not.toBe(true);
|
|
764
|
+
expect(system).toBeDefined();
|
|
765
|
+
expect(system?.unique).not.toBe(true);
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
test("indexes projectId, name and queueIdentifier on their own", () => {
|
|
769
|
+
for (const column of ["projectId", "name", "queueIdentifier"]) {
|
|
770
|
+
expect({
|
|
771
|
+
column,
|
|
772
|
+
indexed: Boolean(propertyIndex(MessageQueue, column)),
|
|
773
|
+
}).toEqual({ column, indexed: true });
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
test("carries every column of the SPEC schema", () => {
|
|
778
|
+
const columns: Array<string> = model.getTableColumns().columns;
|
|
779
|
+
|
|
780
|
+
for (const column of [
|
|
781
|
+
...MESSAGE_QUEUE_USER_CREATE_COLUMNS,
|
|
782
|
+
...MESSAGE_QUEUE_ROOT_ONLY_COLUMNS,
|
|
783
|
+
]) {
|
|
784
|
+
expect({ column, present: columns.includes(column) }).toEqual({
|
|
785
|
+
column,
|
|
786
|
+
present: true,
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// No retention / AI / workload / collector columns, and no consumer groups.
|
|
791
|
+
for (const column of [
|
|
792
|
+
"retainTelemetryDataForDays",
|
|
793
|
+
"telemetryRetentionConfig",
|
|
794
|
+
"isAiInvestigationEnabled",
|
|
795
|
+
"aiRemediationMode",
|
|
796
|
+
"workloadIdentifier",
|
|
797
|
+
"memberEntityKeys",
|
|
798
|
+
"otelCollectorStatus",
|
|
799
|
+
"collectorLastSeenAt",
|
|
800
|
+
"consumerGroups",
|
|
801
|
+
"databaseIdentifier",
|
|
802
|
+
"dbSystem",
|
|
803
|
+
]) {
|
|
804
|
+
expect(columns).not.toContain(column);
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
test("every declared column is accounted for as user-creatable or root-only", () => {
|
|
809
|
+
/*
|
|
810
|
+
* A new column must be placed in one of the two lists above on
|
|
811
|
+
* purpose - which is what decides whether a manual create can set it.
|
|
812
|
+
*/
|
|
813
|
+
const accounted: Set<string> = new Set([
|
|
814
|
+
...MESSAGE_QUEUE_USER_CREATE_COLUMNS,
|
|
815
|
+
...MESSAGE_QUEUE_ROOT_ONLY_COLUMNS,
|
|
816
|
+
]);
|
|
817
|
+
const unaccounted: Array<string> = ownColumns(model).filter(
|
|
818
|
+
(column: string): boolean => {
|
|
819
|
+
return !accounted.has(column);
|
|
820
|
+
},
|
|
821
|
+
);
|
|
822
|
+
|
|
823
|
+
expect(unaccounted).toEqual([]);
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
test("column types and widths follow the SPEC", () => {
|
|
827
|
+
const expectations: Array<
|
|
828
|
+
[string, TableColumnType, boolean, number | undefined]
|
|
829
|
+
> = [
|
|
830
|
+
// column, type, required, varchar length
|
|
831
|
+
["name", TableColumnType.ShortText, true, ColumnLength.ShortText],
|
|
832
|
+
["description", TableColumnType.LongText, false, ColumnLength.LongText],
|
|
833
|
+
[
|
|
834
|
+
"queueIdentifier",
|
|
835
|
+
TableColumnType.LongText,
|
|
836
|
+
true,
|
|
837
|
+
ColumnLength.LongText,
|
|
838
|
+
],
|
|
839
|
+
[
|
|
840
|
+
"messagingSystem",
|
|
841
|
+
TableColumnType.ShortText,
|
|
842
|
+
true,
|
|
843
|
+
ColumnLength.ShortText,
|
|
844
|
+
],
|
|
845
|
+
[
|
|
846
|
+
"destinationName",
|
|
847
|
+
TableColumnType.LongText,
|
|
848
|
+
true,
|
|
849
|
+
ColumnLength.LongText,
|
|
850
|
+
],
|
|
851
|
+
[
|
|
852
|
+
"brokerScope",
|
|
853
|
+
TableColumnType.ShortText,
|
|
854
|
+
false,
|
|
855
|
+
ColumnLength.ShortText,
|
|
856
|
+
],
|
|
857
|
+
[
|
|
858
|
+
"brokerAddress",
|
|
859
|
+
TableColumnType.LongText,
|
|
860
|
+
false,
|
|
861
|
+
ColumnLength.LongText,
|
|
862
|
+
],
|
|
863
|
+
[
|
|
864
|
+
"discoverySource",
|
|
865
|
+
TableColumnType.ShortText,
|
|
866
|
+
false,
|
|
867
|
+
ColumnLength.ShortText,
|
|
868
|
+
],
|
|
869
|
+
["lastSeenAt", TableColumnType.Date, false, undefined],
|
|
870
|
+
["brokerMetricsLastSeenAt", TableColumnType.Date, false, undefined],
|
|
871
|
+
["autoArchivedAt", TableColumnType.Date, false, undefined],
|
|
872
|
+
["manuallyRestoredAt", TableColumnType.Date, false, undefined],
|
|
873
|
+
["automaticAssignments", TableColumnType.JSON, false, undefined],
|
|
874
|
+
["archivedAt", TableColumnType.Date, false, undefined],
|
|
875
|
+
["createdByUserId", TableColumnType.ObjectID, false, undefined],
|
|
876
|
+
];
|
|
877
|
+
|
|
878
|
+
for (const [column, type, required, length] of expectations) {
|
|
879
|
+
const metadata: TableColumnMetadata =
|
|
880
|
+
model.getTableColumnMetadata(column);
|
|
881
|
+
const options: ColumnMetadataArgs["options"] = columnArgs(
|
|
882
|
+
MessageQueue,
|
|
883
|
+
column,
|
|
884
|
+
).options;
|
|
885
|
+
|
|
886
|
+
expect({ column, type: metadata.type }).toEqual({ column, type });
|
|
887
|
+
expect({ column, required: Boolean(metadata.required) }).toEqual({
|
|
888
|
+
column,
|
|
889
|
+
required,
|
|
890
|
+
});
|
|
891
|
+
expect({ column, nullable: options.nullable }).toEqual({
|
|
892
|
+
column,
|
|
893
|
+
nullable: !required,
|
|
894
|
+
});
|
|
895
|
+
expect({ column, length: options.length }).toEqual({ column, length });
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
test("the widths hold everything the identity module can produce", () => {
|
|
900
|
+
/*
|
|
901
|
+
* A resolved destination is at most 255 characters, a display name
|
|
902
|
+
* 100 and an identifier 500 - a value past its column would fail the
|
|
903
|
+
* insert rather than be stored.
|
|
904
|
+
*/
|
|
905
|
+
expect(MESSAGE_QUEUE_DESTINATION_MAX_LENGTH).toBeLessThanOrEqual(
|
|
906
|
+
ColumnLength.LongText,
|
|
907
|
+
);
|
|
908
|
+
expect(MESSAGE_QUEUE_IDENTIFIER_MAX_LENGTH).toBeLessThanOrEqual(
|
|
909
|
+
ColumnLength.LongText,
|
|
910
|
+
);
|
|
911
|
+
expect(MESSAGE_QUEUE_DISPLAY_NAME_MAX_LENGTH).toBeLessThanOrEqual(
|
|
912
|
+
ColumnLength.ShortText,
|
|
913
|
+
);
|
|
914
|
+
|
|
915
|
+
// Every catalogued system name fits the ShortText system column.
|
|
916
|
+
for (const descriptor of MESSAGING_SYSTEMS) {
|
|
917
|
+
expect(descriptor.system.length).toBeLessThanOrEqual(
|
|
918
|
+
ColumnLength.ShortText,
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
test("automaticAssignments is jsonb; new rows are not archived and carry no invented defaults", () => {
|
|
924
|
+
expect(
|
|
925
|
+
columnArgs(MessageQueue, "automaticAssignments").options.type,
|
|
926
|
+
).toBe("jsonb");
|
|
927
|
+
expect(columnArgs(MessageQueue, "isArchived").options.default).toBe(
|
|
928
|
+
false,
|
|
929
|
+
);
|
|
930
|
+
expect(columnArgs(MessageQueue, "isArchived").options.nullable).toBe(
|
|
931
|
+
false,
|
|
932
|
+
);
|
|
933
|
+
expect(model.isDefaultValueColumn("isArchived")).toBe(true);
|
|
934
|
+
|
|
935
|
+
// No DB default where "unknown" must stay NULL.
|
|
936
|
+
for (const column of [
|
|
937
|
+
"lastSeenAt",
|
|
938
|
+
"brokerMetricsLastSeenAt",
|
|
939
|
+
"autoArchivedAt",
|
|
940
|
+
"manuallyRestoredAt",
|
|
941
|
+
"automaticAssignments",
|
|
942
|
+
"discoverySource",
|
|
943
|
+
"brokerScope",
|
|
944
|
+
"brokerAddress",
|
|
945
|
+
]) {
|
|
946
|
+
expect({
|
|
947
|
+
column,
|
|
948
|
+
default: columnArgs(MessageQueue, column).options.default,
|
|
949
|
+
}).toEqual({ column, default: undefined });
|
|
950
|
+
}
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
test("the identifier column documents the family-keyed form, and its example is one", () => {
|
|
954
|
+
const metadata: TableColumnMetadata =
|
|
955
|
+
model.getTableColumnMetadata("queueIdentifier");
|
|
956
|
+
|
|
957
|
+
expect(metadata.description).toContain("family");
|
|
958
|
+
expect(metadata.description).toContain("jms");
|
|
959
|
+
|
|
960
|
+
const example: string = String(metadata.example);
|
|
961
|
+
const parsed: MessageQueueIdentity | null =
|
|
962
|
+
parseMessageQueueIdentifier(example);
|
|
963
|
+
|
|
964
|
+
expect(parsed).not.toBeNull();
|
|
965
|
+
expect(buildMessageQueueIdentifier(parsed!)).toBe(example);
|
|
966
|
+
expect(example).toBe(
|
|
967
|
+
buildMessageQueueIdentifier({
|
|
968
|
+
system: "kafka",
|
|
969
|
+
brokerScope: "",
|
|
970
|
+
destination: "orders.created",
|
|
971
|
+
}),
|
|
972
|
+
);
|
|
973
|
+
});
|
|
974
|
+
|
|
975
|
+
test("the messaging system column names catalogued systems in its example", () => {
|
|
976
|
+
const metadata: TableColumnMetadata =
|
|
977
|
+
model.getTableColumnMetadata("messagingSystem");
|
|
978
|
+
const systems: Array<string> = MESSAGING_SYSTEMS.map(
|
|
979
|
+
(descriptor: MessagingSystemDescriptor): string => {
|
|
980
|
+
return descriptor.system;
|
|
981
|
+
},
|
|
982
|
+
);
|
|
983
|
+
|
|
984
|
+
expect(systems).toContain(String(metadata.example));
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
test("slug is computed from name and is never user-writable", () => {
|
|
988
|
+
expect(model.getSlugifyColumn()).toBe("name");
|
|
989
|
+
expect(model.getSaveSlugToColumn()).toBe("slug");
|
|
990
|
+
expect(model.getTableColumnMetadata("slug").computed).toBe(true);
|
|
991
|
+
expect(model.getTableColumnMetadata("slug").type).toBe(
|
|
992
|
+
TableColumnType.Slug,
|
|
993
|
+
);
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
test("table access control: Settings / project roles plus the granular MessageQueue permissions", () => {
|
|
997
|
+
const writeRoles: Array<Permission> = [
|
|
998
|
+
Permission.ProjectOwner,
|
|
999
|
+
Permission.ProjectAdmin,
|
|
1000
|
+
Permission.ProjectMember,
|
|
1001
|
+
Permission.SettingsAdmin,
|
|
1002
|
+
Permission.SettingsMember,
|
|
1003
|
+
];
|
|
1004
|
+
|
|
1005
|
+
expect(sorted(model.getCreatePermissions())).toEqual(
|
|
1006
|
+
sorted([...writeRoles, Permission.CreateMessageQueue]),
|
|
1007
|
+
);
|
|
1008
|
+
expect(sorted(model.getUpdatePermissions())).toEqual(
|
|
1009
|
+
sorted([...writeRoles, Permission.EditMessageQueue]),
|
|
1010
|
+
);
|
|
1011
|
+
expect(sorted(model.getDeletePermissions())).toEqual(
|
|
1012
|
+
sorted([...writeRoles, Permission.DeleteMessageQueue]),
|
|
1013
|
+
);
|
|
1014
|
+
expect(sorted(model.getReadPermissions())).toEqual(
|
|
1015
|
+
sorted([
|
|
1016
|
+
Permission.ProjectOwner,
|
|
1017
|
+
Permission.ProjectAdmin,
|
|
1018
|
+
Permission.ProjectMember,
|
|
1019
|
+
Permission.Viewer,
|
|
1020
|
+
Permission.SettingsAdmin,
|
|
1021
|
+
Permission.SettingsMember,
|
|
1022
|
+
Permission.SettingsViewer,
|
|
1023
|
+
Permission.ReadMessageQueue,
|
|
1024
|
+
]),
|
|
1025
|
+
);
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
test("is label-access-controlled and not a telemetry-owning operational resource", () => {
|
|
1029
|
+
expect(model.accessControlColumn).toBe("labels");
|
|
1030
|
+
expect(model.enableWorkflowOn).toBeFalsy();
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
test.each(MESSAGE_QUEUE_USER_CREATE_COLUMNS)(
|
|
1034
|
+
"%s is creatable by exactly the table's create roles (COLUMN ACL RULE)",
|
|
1035
|
+
(column: string) => {
|
|
1036
|
+
/*
|
|
1037
|
+
* DatabaseService.create runs onBeforeCreate BEFORE
|
|
1038
|
+
* ModelPermission.checkCreatePermissions. A column the service sets
|
|
1039
|
+
* during a manual create that the caller may not create turns every
|
|
1040
|
+
* manual create into "User is not allowed to create on <column>".
|
|
1041
|
+
*/
|
|
1042
|
+
expect(sorted(columnAccess(model, column).create || [])).toEqual(
|
|
1043
|
+
sorted(model.getCreatePermissions()),
|
|
1044
|
+
);
|
|
1045
|
+
},
|
|
1046
|
+
);
|
|
1047
|
+
|
|
1048
|
+
test.each(MESSAGE_QUEUE_ROOT_ONLY_COLUMNS)(
|
|
1049
|
+
"%s is root-only: nobody creates or updates it through the API",
|
|
1050
|
+
(column: string) => {
|
|
1051
|
+
const accessControl: ColumnAccessControl = columnAccess(model, column);
|
|
1052
|
+
|
|
1053
|
+
expect(accessControl.create || []).toEqual([]);
|
|
1054
|
+
expect(accessControl.update || []).toEqual([]);
|
|
1055
|
+
},
|
|
1056
|
+
);
|
|
1057
|
+
|
|
1058
|
+
test("identity columns are creatable but never updatable", () => {
|
|
1059
|
+
/*
|
|
1060
|
+
* Re-pointing a row at another system, namespace or destination by
|
|
1061
|
+
* editing it would silently hand its history to another queue.
|
|
1062
|
+
* messagingSystem changes only by discovery's refinement, as root.
|
|
1063
|
+
*/
|
|
1064
|
+
for (const column of [
|
|
1065
|
+
"projectId",
|
|
1066
|
+
"queueIdentifier",
|
|
1067
|
+
"messagingSystem",
|
|
1068
|
+
"destinationName",
|
|
1069
|
+
"brokerScope",
|
|
1070
|
+
"discoverySource",
|
|
1071
|
+
"createdByUserId",
|
|
1072
|
+
]) {
|
|
1073
|
+
expect({ column, update: columnAccess(model, column).update }).toEqual({
|
|
1074
|
+
column,
|
|
1075
|
+
update: [],
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
test.each(MESSAGE_QUEUE_USER_UPDATE_COLUMNS)(
|
|
1081
|
+
"%s is updatable by exactly the table's update roles",
|
|
1082
|
+
(column: string) => {
|
|
1083
|
+
expect(sorted(columnAccess(model, column).update || [])).toEqual(
|
|
1084
|
+
sorted(model.getUpdatePermissions()),
|
|
1085
|
+
);
|
|
1086
|
+
},
|
|
1087
|
+
);
|
|
1088
|
+
|
|
1089
|
+
test("no other column is user-updatable", () => {
|
|
1090
|
+
const updatable: Array<string> = ownColumns(model).filter(
|
|
1091
|
+
(column: string): boolean => {
|
|
1092
|
+
return (columnAccess(model, column).update || []).length > 0;
|
|
1093
|
+
},
|
|
1094
|
+
);
|
|
1095
|
+
|
|
1096
|
+
expect(updatable.sort()).toEqual(
|
|
1097
|
+
[...MESSAGE_QUEUE_USER_UPDATE_COLUMNS].sort(),
|
|
1098
|
+
);
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1101
|
+
test("every column is readable by exactly the table's read roles", () => {
|
|
1102
|
+
for (const column of ownColumns(model)) {
|
|
1103
|
+
expect({
|
|
1104
|
+
column,
|
|
1105
|
+
read: sorted(columnAccess(model, column).read || []),
|
|
1106
|
+
}).toEqual({ column, read: sorted(model.getReadPermissions()) });
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
|
|
1110
|
+
describe("against the real permission checks", () => {
|
|
1111
|
+
test.each([
|
|
1112
|
+
["ProjectOwner", [Permission.ProjectOwner]],
|
|
1113
|
+
["ProjectMember", [Permission.ProjectMember]],
|
|
1114
|
+
["SettingsAdmin", [Permission.SettingsAdmin]],
|
|
1115
|
+
["SettingsMember", [Permission.SettingsMember]],
|
|
1116
|
+
["CreateMessageQueue only", [Permission.CreateMessageQueue]],
|
|
1117
|
+
])(
|
|
1118
|
+
"a manual create as %s passes table and column checks",
|
|
1119
|
+
(_label: string, permissions: Array<Permission>) => {
|
|
1120
|
+
expect(
|
|
1121
|
+
checkCreate(MessageQueue, manualCreateData(), permissions),
|
|
1122
|
+
).not.toThrow();
|
|
1123
|
+
},
|
|
1124
|
+
);
|
|
1125
|
+
|
|
1126
|
+
test("a manual create with labels and an archive flag passes too", () => {
|
|
1127
|
+
const data: MessageQueue = manualCreateData();
|
|
1128
|
+
const label: Label = new Label();
|
|
1129
|
+
label._id = ObjectID.generate().toString();
|
|
1130
|
+
data.labels = [label];
|
|
1131
|
+
data.isArchived = false;
|
|
1132
|
+
|
|
1133
|
+
expect(
|
|
1134
|
+
checkCreate(MessageQueue, data, [Permission.CreateMessageQueue]),
|
|
1135
|
+
).not.toThrow();
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
test("a Viewer or SettingsViewer may not add a queue", () => {
|
|
1139
|
+
for (const permission of [
|
|
1140
|
+
Permission.Viewer,
|
|
1141
|
+
Permission.SettingsViewer,
|
|
1142
|
+
]) {
|
|
1143
|
+
expect(
|
|
1144
|
+
checkCreate(MessageQueue, manualCreateData(), [permission]),
|
|
1145
|
+
).toThrow();
|
|
1146
|
+
}
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
test.each(
|
|
1150
|
+
MESSAGE_QUEUE_ROOT_ONLY_COLUMNS.filter((column: string): boolean => {
|
|
1151
|
+
// slug is a Slug column, which ColumnPermission always skips.
|
|
1152
|
+
return column !== "slug";
|
|
1153
|
+
}),
|
|
1154
|
+
)("a non-root create carrying %s is refused", (column: string) => {
|
|
1155
|
+
const data: MessageQueue = manualCreateData();
|
|
1156
|
+
const metadata: TableColumnMetadata =
|
|
1157
|
+
model.getTableColumnMetadata(column);
|
|
1158
|
+
let value: unknown = "value";
|
|
1159
|
+
|
|
1160
|
+
if (metadata.type === TableColumnType.ObjectID) {
|
|
1161
|
+
value = ObjectID.generate();
|
|
1162
|
+
} else if (metadata.type === TableColumnType.Date) {
|
|
1163
|
+
value = new Date();
|
|
1164
|
+
} else if (metadata.type === TableColumnType.JSON) {
|
|
1165
|
+
value = { labelIds: [ObjectID.generate().toString()] };
|
|
1166
|
+
} else if (metadata.type === TableColumnType.Entity) {
|
|
1167
|
+
value = { _id: ObjectID.generate().toString() };
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
(data as unknown as Record<string, unknown>)[column] = value;
|
|
1171
|
+
|
|
1172
|
+
expect(
|
|
1173
|
+
checkColumns(
|
|
1174
|
+
MessageQueue,
|
|
1175
|
+
data,
|
|
1176
|
+
[Permission.ProjectOwner],
|
|
1177
|
+
DatabaseRequestType.Create,
|
|
1178
|
+
),
|
|
1179
|
+
).toThrow(`User is not allowed to create on ${column} column of Queue`);
|
|
1180
|
+
});
|
|
1181
|
+
|
|
1182
|
+
test("an update may rename, describe and archive", () => {
|
|
1183
|
+
const data: MessageQueue = new MessageQueue();
|
|
1184
|
+
data.name = "Orders (prod)";
|
|
1185
|
+
data.description = "Renamed";
|
|
1186
|
+
data.isArchived = true;
|
|
1187
|
+
|
|
1188
|
+
expect(
|
|
1189
|
+
checkColumns(
|
|
1190
|
+
MessageQueue,
|
|
1191
|
+
data,
|
|
1192
|
+
[Permission.EditMessageQueue],
|
|
1193
|
+
DatabaseRequestType.Update,
|
|
1194
|
+
),
|
|
1195
|
+
).not.toThrow();
|
|
1196
|
+
});
|
|
1197
|
+
|
|
1198
|
+
test.each([
|
|
1199
|
+
"queueIdentifier",
|
|
1200
|
+
"messagingSystem",
|
|
1201
|
+
"destinationName",
|
|
1202
|
+
"brokerScope",
|
|
1203
|
+
"discoverySource",
|
|
1204
|
+
"brokerAddress",
|
|
1205
|
+
])(
|
|
1206
|
+
"an update of %s is refused even for a project owner",
|
|
1207
|
+
(column: string) => {
|
|
1208
|
+
const data: MessageQueue = new MessageQueue();
|
|
1209
|
+
(data as unknown as Record<string, unknown>)[column] = "x";
|
|
1210
|
+
|
|
1211
|
+
expect(
|
|
1212
|
+
checkColumns(
|
|
1213
|
+
MessageQueue,
|
|
1214
|
+
data,
|
|
1215
|
+
[Permission.ProjectOwner],
|
|
1216
|
+
DatabaseRequestType.Update,
|
|
1217
|
+
),
|
|
1218
|
+
).toThrow(
|
|
1219
|
+
`User is not allowed to update on ${column} column of Queue`,
|
|
1220
|
+
);
|
|
1221
|
+
},
|
|
1222
|
+
);
|
|
1223
|
+
|
|
1224
|
+
test("a reader may not archive a queue", () => {
|
|
1225
|
+
const data: MessageQueue = new MessageQueue();
|
|
1226
|
+
data.isArchived = true;
|
|
1227
|
+
|
|
1228
|
+
expect(
|
|
1229
|
+
checkColumns(
|
|
1230
|
+
MessageQueue,
|
|
1231
|
+
data,
|
|
1232
|
+
[Permission.ReadMessageQueue],
|
|
1233
|
+
DatabaseRequestType.Update,
|
|
1234
|
+
),
|
|
1235
|
+
).toThrow(BadDataException);
|
|
1236
|
+
});
|
|
1237
|
+
});
|
|
1238
|
+
|
|
1239
|
+
test("labels drive access control through the MessageQueueLabel join table", () => {
|
|
1240
|
+
expect(model.accessControlColumn).toBe("labels");
|
|
1241
|
+
|
|
1242
|
+
const joinTable: JoinTableMetadataArgs = joinTableArgs(
|
|
1243
|
+
MessageQueue,
|
|
1244
|
+
"labels",
|
|
1245
|
+
);
|
|
1246
|
+
|
|
1247
|
+
expect(joinTable.name).toBe("MessageQueueLabel");
|
|
1248
|
+
expect(joinTable.joinColumns?.[0]?.name).toBe("messageQueueId");
|
|
1249
|
+
expect(joinTable.inverseJoinColumns?.[0]?.name).toBe("labelId");
|
|
1250
|
+
expect(model.getTableColumnMetadata("labels").modelType).toBe(Label);
|
|
1251
|
+
expect(relationArgs(MessageQueue, "labels").relationType).toBe(
|
|
1252
|
+
"many-to-many",
|
|
1253
|
+
);
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
test("the project relation cascades; the user relations survive their user", () => {
|
|
1257
|
+
expect(relationArgs(MessageQueue, "project").options.onDelete).toBe(
|
|
1258
|
+
"CASCADE",
|
|
1259
|
+
);
|
|
1260
|
+
|
|
1261
|
+
for (const [property, idColumn] of [
|
|
1262
|
+
["createdByUser", "createdByUserId"],
|
|
1263
|
+
["archivedByUser", "archivedByUserId"],
|
|
1264
|
+
["deletedByUser", "deletedByUserId"],
|
|
1265
|
+
] as Array<[string, string]>) {
|
|
1266
|
+
expect(relationArgs(MessageQueue, property).options.onDelete).toBe(
|
|
1267
|
+
"SET NULL",
|
|
1268
|
+
);
|
|
1269
|
+
expect(joinColumnArgs(MessageQueue, property).name).toBe(idColumn);
|
|
1270
|
+
expect(model.getTableColumnMetadata(property).modelType).toBe(User);
|
|
1271
|
+
}
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
test("the identity and liveness columns can be read through a relation query", () => {
|
|
1275
|
+
for (const column of [
|
|
1276
|
+
"projectId",
|
|
1277
|
+
"name",
|
|
1278
|
+
"description",
|
|
1279
|
+
"queueIdentifier",
|
|
1280
|
+
"messagingSystem",
|
|
1281
|
+
"destinationName",
|
|
1282
|
+
"brokerScope",
|
|
1283
|
+
"discoverySource",
|
|
1284
|
+
"lastSeenAt",
|
|
1285
|
+
]) {
|
|
1286
|
+
expect({
|
|
1287
|
+
column,
|
|
1288
|
+
canRead: Boolean(
|
|
1289
|
+
model.getTableColumnMetadata(column).canReadOnRelationQuery,
|
|
1290
|
+
),
|
|
1291
|
+
}).toEqual({ column, canRead: true });
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
|
|
1295
|
+
test("the initialisers are in place - BaseModel enumerates own properties", () => {
|
|
1296
|
+
for (const column of ownColumns(model)) {
|
|
1297
|
+
expect({
|
|
1298
|
+
column,
|
|
1299
|
+
own: Object.prototype.hasOwnProperty.call(model, column),
|
|
1300
|
+
}).toEqual({ column, own: true });
|
|
1301
|
+
}
|
|
1302
|
+
});
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
describe("discovery sources", () => {
|
|
1306
|
+
test("are exactly traces, broker-metrics and manual", () => {
|
|
1307
|
+
expect([...MESSAGE_QUEUE_DISCOVERY_SOURCES]).toEqual([
|
|
1308
|
+
"traces",
|
|
1309
|
+
"broker-metrics",
|
|
1310
|
+
"manual",
|
|
1311
|
+
]);
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
test.each([
|
|
1315
|
+
["traces", "Application traces"],
|
|
1316
|
+
["broker-metrics", "Broker metrics"],
|
|
1317
|
+
["manual", "Added manually"],
|
|
1318
|
+
[" Manual ", "Added manually"],
|
|
1319
|
+
["something-else", "something-else"],
|
|
1320
|
+
// A Map lookup: an inherited property name is shown as it is.
|
|
1321
|
+
["constructor", "constructor"],
|
|
1322
|
+
["", ""],
|
|
1323
|
+
])("labels %p as %p", (source: string, label: string) => {
|
|
1324
|
+
expect(getMessageQueueDiscoverySourceLabel(source)).toBe(label);
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
test("labels a non-string as nothing", () => {
|
|
1328
|
+
expect(getMessageQueueDiscoverySourceLabel(undefined)).toBe("");
|
|
1329
|
+
expect(getMessageQueueDiscoverySourceLabel(null)).toBe("");
|
|
1330
|
+
expect(getMessageQueueDiscoverySourceLabel(42)).toBe("");
|
|
1331
|
+
});
|
|
1332
|
+
|
|
1333
|
+
test("the discoverySource column documents every source", () => {
|
|
1334
|
+
const description: string =
|
|
1335
|
+
new MessageQueue().getTableColumnMetadata("discoverySource")
|
|
1336
|
+
.description || "";
|
|
1337
|
+
|
|
1338
|
+
for (const source of MESSAGE_QUEUE_DISCOVERY_SOURCES) {
|
|
1339
|
+
expect(description).toContain(source);
|
|
1340
|
+
}
|
|
1341
|
+
});
|
|
1342
|
+
});
|
|
1343
|
+
|
|
1344
|
+
describe("children of MessageQueue", () => {
|
|
1345
|
+
test.each(MESSAGE_QUEUE_CHILDREN)(
|
|
1346
|
+
"%p.%s cascades on queue delete and carries a required messageQueueId",
|
|
1347
|
+
(modelType: ModelType, property: string) => {
|
|
1348
|
+
const relation: RelationMetadataArgs = relationArgs(
|
|
1349
|
+
modelType,
|
|
1350
|
+
property,
|
|
1351
|
+
);
|
|
1352
|
+
|
|
1353
|
+
expect(relation.relationType).toBe("many-to-one");
|
|
1354
|
+
expect(relation.options.onDelete).toBe("CASCADE");
|
|
1355
|
+
|
|
1356
|
+
const model: BaseModel = new modelType();
|
|
1357
|
+
|
|
1358
|
+
expect(model.getTableColumns().columns).toContain("messageQueueId");
|
|
1359
|
+
expect(
|
|
1360
|
+
model.getTableColumnMetadata(property).manyToOneRelationColumn,
|
|
1361
|
+
).toBe("messageQueueId");
|
|
1362
|
+
expect(model.getTableColumnMetadata(property).modelType).toBe(
|
|
1363
|
+
MessageQueue,
|
|
1364
|
+
);
|
|
1365
|
+
expect(model.getTableColumnMetadata("messageQueueId").required).toBe(
|
|
1366
|
+
true,
|
|
1367
|
+
);
|
|
1368
|
+
expect(columnArgs(modelType, "messageQueueId").options.nullable).toBe(
|
|
1369
|
+
false,
|
|
1370
|
+
);
|
|
1371
|
+
expect(joinColumnArgs(modelType, property).name).toBe("messageQueueId");
|
|
1372
|
+
},
|
|
1373
|
+
);
|
|
1374
|
+
|
|
1375
|
+
test.each([
|
|
1376
|
+
[
|
|
1377
|
+
MessageQueueOwnerTeam,
|
|
1378
|
+
"teamId",
|
|
1379
|
+
"This team is already an owner of this queue.",
|
|
1380
|
+
],
|
|
1381
|
+
[
|
|
1382
|
+
MessageQueueOwnerUser,
|
|
1383
|
+
"userId",
|
|
1384
|
+
"This user is already an owner of this queue.",
|
|
1385
|
+
],
|
|
1386
|
+
] as Array<[ModelType, string, string]>)(
|
|
1387
|
+
"%p allows one row per (queue, owner, project)",
|
|
1388
|
+
(modelType: ModelType, ownerColumn: string, message: string) => {
|
|
1389
|
+
const unique: IndexMetadataArgs | undefined = indexOnColumns(
|
|
1390
|
+
modelType,
|
|
1391
|
+
["messageQueueId", ownerColumn, "projectId"],
|
|
1392
|
+
);
|
|
1393
|
+
|
|
1394
|
+
expect(unique).toBeDefined();
|
|
1395
|
+
expect(unique?.unique).toBe(true);
|
|
1396
|
+
|
|
1397
|
+
const constraints: Array<UniqueColumnsTogetherMetadata> =
|
|
1398
|
+
new modelType().getUniqueColumnsTogether();
|
|
1399
|
+
|
|
1400
|
+
expect(constraints).toHaveLength(1);
|
|
1401
|
+
expect(constraints[0]!.columnNames).toEqual([
|
|
1402
|
+
"messageQueueId",
|
|
1403
|
+
ownerColumn,
|
|
1404
|
+
"projectId",
|
|
1405
|
+
]);
|
|
1406
|
+
expect(constraints[0]!.errorMessage).toBe(message);
|
|
1407
|
+
},
|
|
1408
|
+
);
|
|
1409
|
+
|
|
1410
|
+
test.each([
|
|
1411
|
+
[MessageQueueOwnerTeam, "team", Team],
|
|
1412
|
+
[MessageQueueOwnerUser, "user", User],
|
|
1413
|
+
] as Array<[ModelType, string, ModelType]>)(
|
|
1414
|
+
"%p's owner relation cascades and its key is required",
|
|
1415
|
+
(modelType: ModelType, property: string, ownerModel: ModelType) => {
|
|
1416
|
+
const model: BaseModel = new modelType();
|
|
1417
|
+
|
|
1418
|
+
expect(model.getTableColumnMetadata(property).modelType).toBe(
|
|
1419
|
+
ownerModel,
|
|
1420
|
+
);
|
|
1421
|
+
expect(relationArgs(modelType, property).options.onDelete).toBe(
|
|
1422
|
+
"CASCADE",
|
|
1423
|
+
);
|
|
1424
|
+
expect(model.getTableColumnMetadata(`${property}Id`).required).toBe(
|
|
1425
|
+
true,
|
|
1426
|
+
);
|
|
1427
|
+
expect(columnArgs(modelType, `${property}Id`).options.nullable).toBe(
|
|
1428
|
+
false,
|
|
1429
|
+
);
|
|
1430
|
+
},
|
|
1431
|
+
);
|
|
1432
|
+
|
|
1433
|
+
test("owner rows carry the isOwnerNotified flag, which only the server sets", () => {
|
|
1434
|
+
// Owner rule assignment stamps it (OwnerRuleAssignment.createOwner).
|
|
1435
|
+
for (const modelType of [
|
|
1436
|
+
MessageQueueOwnerTeam,
|
|
1437
|
+
MessageQueueOwnerUser,
|
|
1438
|
+
] as Array<ModelType>) {
|
|
1439
|
+
const model: BaseModel = new modelType();
|
|
1440
|
+
|
|
1441
|
+
expect(model.getTableColumns().columns).toContain("isOwnerNotified");
|
|
1442
|
+
expect(columnAccess(model, "isOwnerNotified").create).toEqual([]);
|
|
1443
|
+
expect(columnArgs(modelType, "isOwnerNotified").options.default).toBe(
|
|
1444
|
+
false,
|
|
1445
|
+
);
|
|
1446
|
+
expect(model.enableWorkflowOn).toBeTruthy();
|
|
1447
|
+
}
|
|
1448
|
+
});
|
|
1449
|
+
|
|
1450
|
+
test.each([
|
|
1451
|
+
[MessageQueueOwnerTeam, "OwnerTeam"],
|
|
1452
|
+
[MessageQueueOwnerUser, "OwnerUser"],
|
|
1453
|
+
] as Array<[ModelType, string]>)(
|
|
1454
|
+
"%p gates on its own %s permissions",
|
|
1455
|
+
(modelType: ModelType, kind: string) => {
|
|
1456
|
+
const model: BaseModel = new modelType();
|
|
1457
|
+
|
|
1458
|
+
expect(model.getCreatePermissions()).toContain(
|
|
1459
|
+
`CreateMessageQueue${kind}` as Permission,
|
|
1460
|
+
);
|
|
1461
|
+
expect(model.getReadPermissions()).toContain(
|
|
1462
|
+
`ReadMessageQueue${kind}` as Permission,
|
|
1463
|
+
);
|
|
1464
|
+
expect(model.getUpdatePermissions()).toContain(
|
|
1465
|
+
`EditMessageQueue${kind}` as Permission,
|
|
1466
|
+
);
|
|
1467
|
+
expect(model.getDeletePermissions()).toContain(
|
|
1468
|
+
`DeleteMessageQueue${kind}` as Permission,
|
|
1469
|
+
);
|
|
1470
|
+
},
|
|
1471
|
+
);
|
|
1472
|
+
|
|
1473
|
+
test.each([
|
|
1474
|
+
[MessageQueueOwnerTeam, [Permission.CreateMessageQueueOwnerTeam]],
|
|
1475
|
+
[MessageQueueOwnerUser, [Permission.CreateMessageQueueOwnerUser]],
|
|
1476
|
+
] as Array<[ModelType, Array<Permission>]>)(
|
|
1477
|
+
"a person adding an owner row to %p passes the real create check",
|
|
1478
|
+
(modelType: ModelType, permissions: Array<Permission>) => {
|
|
1479
|
+
const data: BaseModel = new modelType();
|
|
1480
|
+
data.setColumnValue("projectId", PROJECT_ID);
|
|
1481
|
+
data.setColumnValue("messageQueueId", ObjectID.generate());
|
|
1482
|
+
data.setColumnValue(
|
|
1483
|
+
modelType === MessageQueueOwnerTeam ? "teamId" : "userId",
|
|
1484
|
+
ObjectID.generate(),
|
|
1485
|
+
);
|
|
1486
|
+
|
|
1487
|
+
expect(checkCreate(modelType, data, permissions)).not.toThrow();
|
|
1488
|
+
expect(
|
|
1489
|
+
checkCreate(modelType, data, [Permission.ReadMessageQueue]),
|
|
1490
|
+
).toThrow();
|
|
1491
|
+
},
|
|
1492
|
+
);
|
|
1493
|
+
});
|
|
1494
|
+
|
|
1495
|
+
describe("rule models", () => {
|
|
1496
|
+
test.each([
|
|
1497
|
+
[MessageQueueLabelRule, "LabelRule"],
|
|
1498
|
+
[MessageQueueOwnerRule, "OwnerRule"],
|
|
1499
|
+
] as Array<[ModelType, string]>)(
|
|
1500
|
+
"%p is a RuleBaseModel whose criteria column follows the table ACL",
|
|
1501
|
+
(modelType: ModelType, kind: string) => {
|
|
1502
|
+
const model: BaseModel = new modelType();
|
|
1503
|
+
|
|
1504
|
+
expect(model).toBeInstanceOf(RuleBaseModel);
|
|
1505
|
+
expect(model.getTableColumnMetadata("criteria").type).toBe(
|
|
1506
|
+
TableColumnType.JSON,
|
|
1507
|
+
);
|
|
1508
|
+
expect(model.getColumnAccessControlFor("criteria")).toEqual({
|
|
1509
|
+
create: model.getCreatePermissions(),
|
|
1510
|
+
read: model.getReadPermissions(),
|
|
1511
|
+
update: model.getUpdatePermissions(),
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
// Rule tables: owners and admins write, members and viewers read.
|
|
1515
|
+
expect(sorted(model.getCreatePermissions())).toEqual(
|
|
1516
|
+
sorted([
|
|
1517
|
+
Permission.ProjectOwner,
|
|
1518
|
+
Permission.ProjectAdmin,
|
|
1519
|
+
`CreateMessageQueue${kind}` as Permission,
|
|
1520
|
+
]),
|
|
1521
|
+
);
|
|
1522
|
+
expect(sorted(model.getReadPermissions())).toEqual(
|
|
1523
|
+
sorted([
|
|
1524
|
+
Permission.ProjectOwner,
|
|
1525
|
+
Permission.ProjectAdmin,
|
|
1526
|
+
Permission.ProjectMember,
|
|
1527
|
+
Permission.Viewer,
|
|
1528
|
+
`ReadMessageQueue${kind}` as Permission,
|
|
1529
|
+
]),
|
|
1530
|
+
);
|
|
1531
|
+
expect(model.enableWorkflowOn).toBeTruthy();
|
|
1532
|
+
},
|
|
1533
|
+
);
|
|
1534
|
+
|
|
1535
|
+
test.each([
|
|
1536
|
+
MessageQueueLabelRule,
|
|
1537
|
+
MessageQueueOwnerRule,
|
|
1538
|
+
] as Array<ModelType>)(
|
|
1539
|
+
"%p matches on exactly the four registered criteria fields",
|
|
1540
|
+
(modelType: ModelType) => {
|
|
1541
|
+
/*
|
|
1542
|
+
* RuleCriteriaFieldRegistry, the rule engines' legacyFields and the
|
|
1543
|
+
* dashboard's match-criteria step all list these four names; a
|
|
1544
|
+
* rename here breaks all three silently.
|
|
1545
|
+
*/
|
|
1546
|
+
const model: BaseModel = new modelType();
|
|
1547
|
+
const columns: Array<string> = model.getTableColumns().columns;
|
|
1548
|
+
|
|
1549
|
+
const matchColumns: Array<string> = columns.filter(
|
|
1550
|
+
(column: string): boolean => {
|
|
1551
|
+
return column.startsWith("messageQueue");
|
|
1552
|
+
},
|
|
1553
|
+
);
|
|
1554
|
+
|
|
1555
|
+
expect(matchColumns.sort()).toEqual([...RULE_CRITERIA_FIELDS].sort());
|
|
1556
|
+
|
|
1557
|
+
expect(model.getTableColumnMetadata("messageQueueLabels").type).toBe(
|
|
1558
|
+
TableColumnType.EntityArray,
|
|
1559
|
+
);
|
|
1560
|
+
expect(
|
|
1561
|
+
model.getTableColumnMetadata("messageQueueLabels").modelType,
|
|
1562
|
+
).toBe(Label);
|
|
1563
|
+
|
|
1564
|
+
for (const pattern of [
|
|
1565
|
+
"messageQueueNamePattern",
|
|
1566
|
+
"messageQueueDescriptionPattern",
|
|
1567
|
+
"messageQueueSystemPattern",
|
|
1568
|
+
]) {
|
|
1569
|
+
expect(model.getTableColumnMetadata(pattern).type).toBe(
|
|
1570
|
+
TableColumnType.LongText,
|
|
1571
|
+
);
|
|
1572
|
+
expect(model.getTableColumnMetadata(pattern).required).toBeFalsy();
|
|
1573
|
+
expect(columnArgs(modelType, pattern).options.nullable).toBe(true);
|
|
1574
|
+
expect(sorted(columnAccess(model, pattern).update || [])).toEqual(
|
|
1575
|
+
sorted(model.getUpdatePermissions()),
|
|
1576
|
+
);
|
|
1577
|
+
}
|
|
1578
|
+
},
|
|
1579
|
+
);
|
|
1580
|
+
|
|
1581
|
+
test.each([
|
|
1582
|
+
[MessageQueueLabelRule, "LabelRule"],
|
|
1583
|
+
[MessageQueueOwnerRule, "OwnerRule"],
|
|
1584
|
+
] as Array<[ModelType, string]>)(
|
|
1585
|
+
"%p: every criteria field is created, read and updated by exactly the table's roles",
|
|
1586
|
+
(modelType: ModelType, kind: string) => {
|
|
1587
|
+
/*
|
|
1588
|
+
* An empty create or read list fails nothing else loudly: the column
|
|
1589
|
+
* drops out of the portable label rule file
|
|
1590
|
+
* (ModelImportExport.getImportExportableColumnNames skips it) - an
|
|
1591
|
+
* exported "Kafka only" rule re-imports as one that matches every
|
|
1592
|
+
* queue - and the rule form's create is refused for anyone who
|
|
1593
|
+
* fills the field in.
|
|
1594
|
+
*/
|
|
1595
|
+
const model: BaseModel = new modelType();
|
|
1596
|
+
|
|
1597
|
+
for (const column of RULE_CRITERIA_FIELDS) {
|
|
1598
|
+
const accessControl: ColumnAccessControl = columnAccess(
|
|
1599
|
+
model,
|
|
1600
|
+
column,
|
|
1601
|
+
);
|
|
1602
|
+
|
|
1603
|
+
expect({
|
|
1604
|
+
column,
|
|
1605
|
+
create: sorted(accessControl.create || []),
|
|
1606
|
+
read: sorted(accessControl.read || []),
|
|
1607
|
+
update: sorted(accessControl.update || []),
|
|
1608
|
+
}).toEqual({
|
|
1609
|
+
column,
|
|
1610
|
+
create: sorted(model.getCreatePermissions()),
|
|
1611
|
+
read: sorted(model.getReadPermissions()),
|
|
1612
|
+
update: sorted(model.getUpdatePermissions()),
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
// And through the real column check, every field set at once.
|
|
1617
|
+
const data: BaseModel = new modelType();
|
|
1618
|
+
const label: Label = new Label();
|
|
1619
|
+
label._id = ObjectID.generate().toString();
|
|
1620
|
+
data.setColumnValue("messageQueueLabels", [label]);
|
|
1621
|
+
data.setColumnValue("messageQueueNamePattern", "^orders\\.");
|
|
1622
|
+
data.setColumnValue("messageQueueDescriptionPattern", "payments");
|
|
1623
|
+
data.setColumnValue("messageQueueSystemPattern", "^kafka$");
|
|
1624
|
+
|
|
1625
|
+
expect(
|
|
1626
|
+
checkColumns(
|
|
1627
|
+
modelType,
|
|
1628
|
+
data,
|
|
1629
|
+
[`CreateMessageQueue${kind}` as Permission],
|
|
1630
|
+
DatabaseRequestType.Create,
|
|
1631
|
+
),
|
|
1632
|
+
).not.toThrow();
|
|
1633
|
+
expect(
|
|
1634
|
+
checkColumns(
|
|
1635
|
+
modelType,
|
|
1636
|
+
data,
|
|
1637
|
+
[`EditMessageQueue${kind}` as Permission],
|
|
1638
|
+
DatabaseRequestType.Update,
|
|
1639
|
+
),
|
|
1640
|
+
).not.toThrow();
|
|
1641
|
+
expect(
|
|
1642
|
+
checkColumns(
|
|
1643
|
+
modelType,
|
|
1644
|
+
data,
|
|
1645
|
+
[`ReadMessageQueue${kind}` as Permission],
|
|
1646
|
+
DatabaseRequestType.Create,
|
|
1647
|
+
),
|
|
1648
|
+
).toThrow(BadDataException);
|
|
1649
|
+
},
|
|
1650
|
+
);
|
|
1651
|
+
|
|
1652
|
+
test("label rule attaches labels through its own join tables", () => {
|
|
1653
|
+
const model: MessageQueueLabelRule = new MessageQueueLabelRule();
|
|
1654
|
+
|
|
1655
|
+
for (const column of [
|
|
1656
|
+
"name",
|
|
1657
|
+
"description",
|
|
1658
|
+
"isEnabled",
|
|
1659
|
+
"labelsToAdd",
|
|
1660
|
+
]) {
|
|
1661
|
+
expect(model.getTableColumns().columns).toContain(column);
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
const matchLabels: JoinTableMetadataArgs = joinTableArgs(
|
|
1665
|
+
MessageQueueLabelRule,
|
|
1666
|
+
"messageQueueLabels",
|
|
1667
|
+
);
|
|
1668
|
+
|
|
1669
|
+
expect(matchLabels.name).toBe("MessageQueueLabelRuleMessageQueueLabel");
|
|
1670
|
+
expect(matchLabels.joinColumns?.[0]?.name).toBe(
|
|
1671
|
+
"messageQueueLabelRuleId",
|
|
1672
|
+
);
|
|
1673
|
+
expect(matchLabels.inverseJoinColumns?.[0]?.name).toBe("labelId");
|
|
1674
|
+
|
|
1675
|
+
const labelsToAdd: JoinTableMetadataArgs = joinTableArgs(
|
|
1676
|
+
MessageQueueLabelRule,
|
|
1677
|
+
"labelsToAdd",
|
|
1678
|
+
);
|
|
1679
|
+
|
|
1680
|
+
expect(labelsToAdd.name).toBe("MessageQueueLabelRuleLabelToAdd");
|
|
1681
|
+
expect(labelsToAdd.joinColumns?.[0]?.name).toBe(
|
|
1682
|
+
"messageQueueLabelRuleId",
|
|
1683
|
+
);
|
|
1684
|
+
});
|
|
1685
|
+
|
|
1686
|
+
test("owner rule assigns users and teams through its own join tables", () => {
|
|
1687
|
+
const model: MessageQueueOwnerRule = new MessageQueueOwnerRule();
|
|
1688
|
+
|
|
1689
|
+
for (const column of [
|
|
1690
|
+
"name",
|
|
1691
|
+
"description",
|
|
1692
|
+
"isEnabled",
|
|
1693
|
+
"notifyOwners",
|
|
1694
|
+
"ownerUsers",
|
|
1695
|
+
"ownerTeams",
|
|
1696
|
+
]) {
|
|
1697
|
+
expect(model.getTableColumns().columns).toContain(column);
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
expect(columnArgs(MessageQueueOwnerRule, "notifyOwners").options).toEqual(
|
|
1701
|
+
expect.objectContaining({ nullable: false, default: true }),
|
|
1702
|
+
);
|
|
1703
|
+
|
|
1704
|
+
expect(
|
|
1705
|
+
joinTableArgs(MessageQueueOwnerRule, "messageQueueLabels").name,
|
|
1706
|
+
).toBe("MessageQueueOwnerRuleMessageQueueLabel");
|
|
1707
|
+
expect(joinTableArgs(MessageQueueOwnerRule, "ownerUsers").name).toBe(
|
|
1708
|
+
"MessageQueueOwnerRuleOwnerUser",
|
|
1709
|
+
);
|
|
1710
|
+
expect(joinTableArgs(MessageQueueOwnerRule, "ownerTeams").name).toBe(
|
|
1711
|
+
"MessageQueueOwnerRuleOwnerTeam",
|
|
1712
|
+
);
|
|
1713
|
+
|
|
1714
|
+
for (const property of [
|
|
1715
|
+
"messageQueueLabels",
|
|
1716
|
+
"ownerUsers",
|
|
1717
|
+
"ownerTeams",
|
|
1718
|
+
]) {
|
|
1719
|
+
expect(
|
|
1720
|
+
joinTableArgs(MessageQueueOwnerRule, property).joinColumns?.[0]?.name,
|
|
1721
|
+
).toBe("messageQueueOwnerRuleId");
|
|
1722
|
+
}
|
|
1723
|
+
});
|
|
1724
|
+
|
|
1725
|
+
test("the patterns tell people a queue's name is its destination and how to match a broker", () => {
|
|
1726
|
+
for (const modelType of [
|
|
1727
|
+
MessageQueueLabelRule,
|
|
1728
|
+
MessageQueueOwnerRule,
|
|
1729
|
+
] as Array<ModelType>) {
|
|
1730
|
+
const model: BaseModel = new modelType();
|
|
1731
|
+
|
|
1732
|
+
expect(
|
|
1733
|
+
model.getTableColumnMetadata("messageQueueNamePattern").description,
|
|
1734
|
+
).toContain("destination");
|
|
1735
|
+
expect(
|
|
1736
|
+
model.getTableColumnMetadata("messageQueueSystemPattern").title,
|
|
1737
|
+
).toBe("Messaging System Pattern");
|
|
1738
|
+
|
|
1739
|
+
const systemDescription: string =
|
|
1740
|
+
model.getTableColumnMetadata("messageQueueSystemPattern")
|
|
1741
|
+
.description || "";
|
|
1742
|
+
|
|
1743
|
+
// Both forms the engines match against are documented.
|
|
1744
|
+
expect(systemDescription).toContain("messaging.system");
|
|
1745
|
+
expect(systemDescription).toContain("Apache Kafka");
|
|
1746
|
+
expect(systemDescription).toContain("^kafka$");
|
|
1747
|
+
}
|
|
1748
|
+
});
|
|
1749
|
+
|
|
1750
|
+
test("deletedBy columns are hidden on rule tables, like every rule table", () => {
|
|
1751
|
+
for (const modelType of [
|
|
1752
|
+
MessageQueueLabelRule,
|
|
1753
|
+
MessageQueueOwnerRule,
|
|
1754
|
+
] as Array<ModelType>) {
|
|
1755
|
+
const model: BaseModel = new modelType();
|
|
1756
|
+
|
|
1757
|
+
for (const column of ["deletedByUser", "deletedByUserId"]) {
|
|
1758
|
+
expect(columnAccess(model, column)).toEqual({
|
|
1759
|
+
create: [],
|
|
1760
|
+
read: [],
|
|
1761
|
+
update: [],
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
});
|
|
1766
|
+
});
|
|
1767
|
+
|
|
1768
|
+
describe("Postgres identifier limits", () => {
|
|
1769
|
+
/*
|
|
1770
|
+
* Postgres silently truncates identifiers to 63 bytes, so two long names
|
|
1771
|
+
* that share a prefix become the same table, and a generated migration
|
|
1772
|
+
* never matches its own output again.
|
|
1773
|
+
*/
|
|
1774
|
+
const LIMIT: number = 63;
|
|
1775
|
+
|
|
1776
|
+
test("every table, join table, join column and named index fits", () => {
|
|
1777
|
+
const names: Array<string> = [];
|
|
1778
|
+
const targets: Array<unknown> = QUEUE_MODELS.map(
|
|
1779
|
+
(spec: QueueModelSpec): unknown => {
|
|
1780
|
+
return spec.modelType;
|
|
1781
|
+
},
|
|
1782
|
+
);
|
|
1783
|
+
|
|
1784
|
+
for (const spec of QUEUE_MODELS) {
|
|
1785
|
+
names.push(spec.tableName);
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
for (const joinTable of getMetadataArgsStorage()
|
|
1789
|
+
.joinTables as Array<JoinTableMetadataArgs>) {
|
|
1790
|
+
if (!targets.includes(joinTable.target)) {
|
|
1791
|
+
continue;
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
names.push(joinTable.name!);
|
|
1795
|
+
|
|
1796
|
+
for (const column of [
|
|
1797
|
+
...(joinTable.joinColumns || []),
|
|
1798
|
+
...(joinTable.inverseJoinColumns || []),
|
|
1799
|
+
]) {
|
|
1800
|
+
names.push(column.name!);
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
for (const target of targets) {
|
|
1805
|
+
for (const index of classIndexes(target as ModelType)) {
|
|
1806
|
+
if (index.name) {
|
|
1807
|
+
names.push(index.name);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
// 5 tables, 6 join tables with 12 columns, 2 named indexes.
|
|
1813
|
+
expect(names.length).toBe(25);
|
|
1814
|
+
|
|
1815
|
+
const tooLong: Array<string> = names.filter((name: string): boolean => {
|
|
1816
|
+
return name.length > LIMIT;
|
|
1817
|
+
});
|
|
1818
|
+
|
|
1819
|
+
expect(tooLong).toEqual([]);
|
|
1820
|
+
});
|
|
1821
|
+
|
|
1822
|
+
test("every column name fits", () => {
|
|
1823
|
+
for (const spec of QUEUE_MODELS) {
|
|
1824
|
+
for (const column of new spec.modelType().getTableColumns().columns) {
|
|
1825
|
+
expect(column.length).toBeLessThanOrEqual(LIMIT);
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
});
|
|
1829
|
+
});
|
|
1830
|
+
|
|
1831
|
+
describe("permissions", () => {
|
|
1832
|
+
test("all 20 permissions exist in the enum and the catalogue", () => {
|
|
1833
|
+
const enumValues: Set<string> = new Set(Object.values(Permission));
|
|
1834
|
+
|
|
1835
|
+
for (const name of MESSAGE_QUEUE_PERMISSIONS) {
|
|
1836
|
+
expect(enumValues.has(name)).toBe(true);
|
|
1837
|
+
expect(Permission[name as keyof typeof Permission]).toBe(name);
|
|
1838
|
+
|
|
1839
|
+
const props: PermissionProps | undefined =
|
|
1840
|
+
PERMISSION_PROPS_BY_NAME.get(name);
|
|
1841
|
+
|
|
1842
|
+
expect(props).toBeDefined();
|
|
1843
|
+
expect(props!.isAssignableToTenant).toBe(true);
|
|
1844
|
+
expect(props!.isRolePermission).toBe(false);
|
|
1845
|
+
expect(props!.group).toBe("Telemetry");
|
|
1846
|
+
expect(props!.title).toContain("Queue");
|
|
1847
|
+
expect(props!.title).not.toContain("MessageQueue");
|
|
1848
|
+
expect(props!.title).not.toContain("Database");
|
|
1849
|
+
expect(props!.description.length).toBeGreaterThan(0);
|
|
1850
|
+
expect(props!.description).not.toContain("Database");
|
|
1851
|
+
expect(props!.description.endsWith(".")).toBe(true);
|
|
1852
|
+
|
|
1853
|
+
// The lookups the permission UI and the error messages use.
|
|
1854
|
+
expect(PermissionHelper.getTitle(name as Permission)).toBe(
|
|
1855
|
+
props!.title,
|
|
1856
|
+
);
|
|
1857
|
+
expect(PermissionHelper.getDescription(name as Permission)).toBe(
|
|
1858
|
+
props!.description,
|
|
1859
|
+
);
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
// No stray members - the catalogue must not sprout a 21st.
|
|
1863
|
+
const members: Array<string> = Array.from(enumValues).filter(
|
|
1864
|
+
(value: string) => {
|
|
1865
|
+
return value.includes("MessageQueue");
|
|
1866
|
+
},
|
|
1867
|
+
);
|
|
1868
|
+
|
|
1869
|
+
expect(members.sort()).toEqual([...MESSAGE_QUEUE_PERMISSIONS].sort());
|
|
1870
|
+
});
|
|
1871
|
+
|
|
1872
|
+
test("each permission appears exactly once in the props catalogue", () => {
|
|
1873
|
+
for (const name of MESSAGE_QUEUE_PERMISSIONS) {
|
|
1874
|
+
expect(
|
|
1875
|
+
PERMISSION_PROPS.filter((props: PermissionProps): boolean => {
|
|
1876
|
+
return props.permission.toString() === name;
|
|
1877
|
+
}),
|
|
1878
|
+
).toHaveLength(1);
|
|
1879
|
+
}
|
|
1880
|
+
});
|
|
1881
|
+
|
|
1882
|
+
test("titles are unique among the catalogue", () => {
|
|
1883
|
+
for (const name of MESSAGE_QUEUE_PERMISSIONS) {
|
|
1884
|
+
const title: string = PERMISSION_PROPS_BY_NAME.get(name)!.title;
|
|
1885
|
+
|
|
1886
|
+
expect(
|
|
1887
|
+
PERMISSION_PROPS.filter((props: PermissionProps): boolean => {
|
|
1888
|
+
return props.title === title;
|
|
1889
|
+
}),
|
|
1890
|
+
).toHaveLength(1);
|
|
1891
|
+
}
|
|
1892
|
+
});
|
|
1893
|
+
|
|
1894
|
+
test("queue Delete/Edit/Read are label-scoped access-control permissions; nothing else is", () => {
|
|
1895
|
+
for (const name of MESSAGE_QUEUE_PERMISSIONS) {
|
|
1896
|
+
const props: PermissionProps = PERMISSION_PROPS_BY_NAME.get(name)!;
|
|
1897
|
+
const labelScoped: boolean = [
|
|
1898
|
+
"DeleteMessageQueue",
|
|
1899
|
+
"EditMessageQueue",
|
|
1900
|
+
"ReadMessageQueue",
|
|
1901
|
+
].includes(name);
|
|
1902
|
+
|
|
1903
|
+
expect({ name, flag: props.isAccessControlPermission }).toEqual({
|
|
1904
|
+
name,
|
|
1905
|
+
flag: labelScoped,
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1908
|
+
});
|
|
1909
|
+
|
|
1910
|
+
test("there is no queue feed, so no feed permissions", () => {
|
|
1911
|
+
for (const name of [
|
|
1912
|
+
"CreateMessageQueueFeed",
|
|
1913
|
+
"EditMessageQueueFeed",
|
|
1914
|
+
"ReadMessageQueueFeed",
|
|
1915
|
+
]) {
|
|
1916
|
+
expect(Object.values(Permission)).not.toContain(name);
|
|
1917
|
+
}
|
|
1918
|
+
});
|
|
1919
|
+
|
|
1920
|
+
test.each(QUEUE_MODELS)(
|
|
1921
|
+
"$name references only real, catalogued permissions",
|
|
1922
|
+
(spec: QueueModelSpec) => {
|
|
1923
|
+
const enumValues: Set<string> = new Set(Object.values(Permission));
|
|
1924
|
+
const model: BaseModel = new spec.modelType();
|
|
1925
|
+
|
|
1926
|
+
for (const permission of allReferencedPermissions(model)) {
|
|
1927
|
+
const value: string = permission.toString();
|
|
1928
|
+
|
|
1929
|
+
expect(enumValues.has(value)).toBe(true);
|
|
1930
|
+
expect(PERMISSION_PROPS_BY_NAME.has(value)).toBe(true);
|
|
1931
|
+
}
|
|
1932
|
+
},
|
|
1933
|
+
);
|
|
1934
|
+
|
|
1935
|
+
test.each(QUEUE_MODELS)(
|
|
1936
|
+
"$name gates on Queue permissions, never on another product's",
|
|
1937
|
+
(spec: QueueModelSpec) => {
|
|
1938
|
+
const model: BaseModel = new spec.modelType();
|
|
1939
|
+
|
|
1940
|
+
for (const permission of allReferencedPermissions(model)) {
|
|
1941
|
+
const value: string = permission.toString();
|
|
1942
|
+
|
|
1943
|
+
expect(value).not.toContain("DatabaseServer");
|
|
1944
|
+
expect(value).not.toContain("Ceph");
|
|
1945
|
+
expect(value).not.toContain("ServerlessFunction");
|
|
1946
|
+
}
|
|
1947
|
+
},
|
|
1948
|
+
);
|
|
1949
|
+
|
|
1950
|
+
test.each(QUEUE_MODELS)(
|
|
1951
|
+
"$name has no column that requires a permission its table does not accept on create",
|
|
1952
|
+
(spec: QueueModelSpec) => {
|
|
1953
|
+
/*
|
|
1954
|
+
* The PermissionCatalogueCoverage sweep, scoped: a granular holder
|
|
1955
|
+
* that passes the table gate must not fail on a column gate.
|
|
1956
|
+
*/
|
|
1957
|
+
const model: BaseModel = new spec.modelType();
|
|
1958
|
+
const roles: Set<string> = new Set(
|
|
1959
|
+
PermissionHelper.getRolePermissionProps().map(
|
|
1960
|
+
(props: PermissionProps): string => {
|
|
1961
|
+
return props.permission.toString();
|
|
1962
|
+
},
|
|
1963
|
+
),
|
|
1964
|
+
);
|
|
1965
|
+
const tableCreate: Set<string> = new Set(
|
|
1966
|
+
sorted(model.getCreatePermissions()),
|
|
1967
|
+
);
|
|
1968
|
+
|
|
1969
|
+
for (const column of ownColumns(model)) {
|
|
1970
|
+
for (const permission of columnAccess(model, column).create || []) {
|
|
1971
|
+
const value: string = permission.toString();
|
|
1972
|
+
|
|
1973
|
+
expect({
|
|
1974
|
+
column,
|
|
1975
|
+
value,
|
|
1976
|
+
ok: roles.has(value) || tableCreate.has(value),
|
|
1977
|
+
}).toEqual({ column, value, ok: true });
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
},
|
|
1981
|
+
);
|
|
1982
|
+
});
|
|
1983
|
+
});
|