@oneuptime/common 14.0.9 → 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/AutoRemediationSuggestion.ts +60 -0
- package/Models/DatabaseModels/CephCluster.ts +225 -0
- package/Models/DatabaseModels/CloudResource.ts +12 -0
- package/Models/DatabaseModels/DatabaseServer.ts +225 -0
- package/Models/DatabaseModels/DockerHost.ts +225 -0
- package/Models/DatabaseModels/DockerSwarmCluster.ts +225 -0
- package/Models/DatabaseModels/GlobalConfig.ts +1 -1
- 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 +225 -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 +16 -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 +225 -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 +225 -0
- package/Models/DatabaseModels/ResourceAiAgent.ts +419 -0
- package/Models/DatabaseModels/RumApplication.ts +12 -0
- package/Models/DatabaseModels/RunnerJob.ts +144 -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 +225 -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/AutoRemediationAPI.ts +392 -1
- package/Server/API/IncidentFormAPI.ts +376 -0
- package/Server/API/ProjectOIDC.ts +16 -25
- package/Server/API/ProjectSSO.ts +16 -24
- package/Server/API/ResourceAiAccessAPI.ts +1669 -0
- 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/1796300000000-AddResourceAiAgents.ts +423 -0
- 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 +10 -0
- package/Server/Infrastructure/Semaphore.ts +22 -0
- package/Server/Middleware/IncidentFormRateLimit.ts +847 -0
- package/Server/Middleware/TelemetryIngest.ts +15 -5
- package/Server/Middleware/UserAuthorization.ts +16 -38
- package/Server/Services/AIService.ts +10 -13
- package/Server/Services/AnalyticsDatabaseService.ts +45 -1
- package/Server/Services/AutoRemediationRuleEngineService.ts +940 -112
- package/Server/Services/CephClusterService.ts +109 -1
- package/Server/Services/DatabaseServerService.ts +116 -1
- package/Server/Services/DockerHostService.ts +109 -1
- package/Server/Services/DockerSwarmClusterService.ts +113 -1
- package/Server/Services/HostService.ts +126 -4
- 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/MetricRecordingRuleService.ts +47 -0
- package/Server/Services/OpenTelemetryIngestService.ts +37 -4
- package/Server/Services/PodmanHostService.ts +109 -1
- package/Server/Services/ProjectService.ts +0 -59
- package/Server/Services/ProxmoxClusterService.ts +110 -1
- package/Server/Services/ResourceAiAccessService.ts +1439 -0
- package/Server/Services/ResourceAiAgentJobService.ts +202 -0
- package/Server/Services/ResourceAiAgentService.ts +2432 -0
- package/Server/Services/RunnerJobService.ts +611 -4
- package/Server/Services/StatusPagePrivateUserService.ts +1 -4
- package/Server/Services/StatusPageService.ts +1 -51
- package/Server/Services/TeamComplianceSettingService.ts +163 -42
- package/Server/Services/TelemetryUsageBillingService.ts +28 -0
- package/Server/Services/TraceRecordingRuleService.ts +33 -0
- package/Server/Services/UserNotificationRuleService.ts +8 -1
- package/Server/Services/VMwareVCenterService.ts +110 -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 +1442 -34
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +968 -34
- package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +25 -1
- package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +622 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAccessContext.ts +271 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.ts +45 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.ts +964 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.ts +333 -0
- package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +882 -0
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +125 -12
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +99 -15
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +99 -15
- 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/AutoRemediation/CommandPlanExecutor.ts +396 -13
- package/Server/Utils/AutoRemediation/RemediationVerifier.ts +35 -2
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +9 -1
- 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/SessionReplay/SessionReplayBudgetMetrics.ts +920 -0
- package/Server/Utils/SessionReplay/SessionReplayUsage.ts +84 -6
- 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/Alert.ts +35 -15
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +33 -15
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +21 -1
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +301 -225
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +38 -16
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +389 -175
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +376 -144
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.ts +163 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.ts +396 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.ts +418 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.ts +152 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.ts +251 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.ts +214 -0
- 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 +267 -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 +23 -6
- 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/InvestigationInfrastructureTools.test.tsx +506 -0
- 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/RemediationSuggestionCardDescription.test.tsx +209 -0
- package/Tests/App/Dashboard/RemediationSuggestionCardResource.test.tsx +317 -0
- package/Tests/App/Dashboard/ResourceAiAccessSettingsUtil.test.ts +921 -0
- package/Tests/App/Dashboard/ResourceAiAgentAttentionServerParity.test.ts +555 -0
- package/Tests/App/Dashboard/ResourceAiAgentInstall.test.ts +860 -0
- package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +2010 -0
- package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +1455 -0
- package/Tests/App/Dashboard/ResourceAiInsightsPage.test.tsx +931 -0
- package/Tests/App/Dashboard/ResourceAiNavigation.test.tsx +575 -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/RunbookStepTypeMaps.test.ts +38 -7
- 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 +61 -10
- package/Tests/Models/DatabaseModels/IncidentFormModel.test.ts +1250 -0
- package/Tests/Models/DatabaseModels/MessageQueueModels.test.ts +1983 -0
- package/Tests/Models/DatabaseModels/ResourceAiAccessColumns.test.ts +738 -0
- package/Tests/Models/DatabaseModels/ResourceAiAgentModel.test.ts +842 -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/AutoRemediationApproveResourceRound.test.ts +835 -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/ResourceAiAccessAPI.test.ts +2730 -0
- 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/AddDatabaseServerTablesMigration.test.ts +60 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentFormsMigration.test.ts +438 -0
- package/Tests/Server/Infrastructure/Postgres/AddResourceAiAgentsMigration.test.ts +659 -0
- package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleNotificationChannelsMigration.test.ts +255 -0
- package/Tests/Server/Infrastructure/Postgres/MessageQueueTablesMigration.test.ts +637 -0
- package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +31 -0
- package/Tests/Server/Middleware/IncidentFormRateLimit.test.ts +1630 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +63 -5
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +103 -5
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +19 -1
- 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/AutoRemediationResourceRuleEngine.test.ts +1279 -0
- package/Tests/Server/Services/DatabaseServerService.test.ts +55 -0
- package/Tests/Server/Services/GroupTelemetryUsageExcludeNames.test.ts +299 -0
- package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +44 -0
- 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/MonitorProbeServiceIntervalScheduling.test.ts +45 -6
- 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/RecordingRuleReservedMetricName.test.ts +171 -0
- package/Tests/Server/Services/ResourceAiAccessChangeAuthorization.test.ts +256 -0
- package/Tests/Server/Services/ResourceAiAccessService.test.ts +1373 -0
- package/Tests/Server/Services/ResourceAiAgentJobService.test.ts +375 -0
- package/Tests/Server/Services/ResourceAiAgentServiceHelpers.test.ts +1114 -0
- package/Tests/Server/Services/ResourceAiAgentServiceLifecycle.test.ts +1050 -0
- package/Tests/Server/Services/ResourceAiAgentServiceRegister.test.ts +2251 -0
- package/Tests/Server/Services/ResourceAiSettingsCreate.test.ts +373 -0
- package/Tests/Server/Services/ResourceAiSettingsPermission.test.ts +1042 -0
- package/Tests/Server/Services/ResourceServiceDeleteCleansUpAi.test.ts +319 -0
- package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +56 -6
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -1
- package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +105 -0
- package/Tests/Server/Services/RunnerJobEnqueueResourceCommand.test.ts +986 -0
- package/Tests/Server/Services/RunnerJobResourceCommandLane.test.ts +211 -0
- package/Tests/Server/Services/RunnerJobResourceCommandTimeoutAndRedaction.test.ts +352 -0
- 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/Services/TelemetryUsageBillingSloExclusion.test.ts +218 -0
- package/Tests/Server/TestingUtils/Services/FakeRunnerJobCount.ts +145 -0
- 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 +694 -0
- package/Tests/Server/Utils/AI/InvestigationInfrastructureReport.test.ts +721 -0
- 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/RemediationCommandTools.test.ts +94 -1
- package/Tests/Server/Utils/AI/RemediationCommandToolsResource.test.ts +1530 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +14 -25
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +1251 -0
- package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +117 -0
- package/Tests/Server/Utils/AI/RemediationResourceCopyParity.test.ts +463 -0
- package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +1004 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceAccessContext.test.ts +266 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.test.ts +1220 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.test.ts +569 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +834 -0
- 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/AutoRemediation/CommandPlanExecutorResource.test.ts +717 -0
- package/Tests/Server/Utils/AutoRemediation/RemediationVerifierResourceFollowUp.test.ts +338 -0
- package/Tests/Server/Utils/EditionEnforcement.test.ts +107 -147
- package/Tests/Server/Utils/Monitor/Criteria/SessionReplayBudgetTemplateCriteria.test.ts +422 -0
- 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/SessionReplay/SessionReplayBudgetMetrics.test.ts +1647 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +143 -1
- 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/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +33 -3
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsAccountNotLinked.test.ts +1093 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.test.ts +1049 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.test.ts +1676 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCards.test.ts +2884 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.test.ts +2490 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmit.test.ts +2293 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmitServerTimezone.test.ts +386 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.test.ts +1477 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.test.ts +1967 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsStripHtmlTags.test.ts +92 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsSubmittedFormRemoval.test.ts +1819 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.test.ts +1033 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezoneServerZone.test.ts +610 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsBotMessageHandling.test.ts +2610 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsCreateCommandsEndToEnd.test.ts +4672 -0
- package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +190 -65
- package/Tests/Types/AI/AIAgentRunLimits.test.ts +255 -0
- package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +10 -4
- package/Tests/Types/AutoRemediation/AiRemediationCommandPlanResourceCommand.test.ts +305 -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/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +277 -13
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +113 -0
- package/Tests/Types/Monitor/RumAlertTemplates.test.ts +773 -2
- package/Tests/Types/ResourceAiAgent/AiResourceType.test.ts +419 -0
- package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +519 -0
- package/Tests/Types/Runbook/RunbookStepType.test.ts +147 -8
- 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/Rum/RecordingHealthDashboard.test.tsx +769 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +81 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +17 -0
- package/Tests/Utils/AiRemediation/Resource/CephCommandPolicy.test.ts +1653 -0
- package/Tests/Utils/AiRemediation/Resource/CephOutputRedaction.test.ts +204 -0
- package/Tests/Utils/AiRemediation/Resource/DatabaseCommandPolicy.test.ts +1077 -0
- package/Tests/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.test.ts +558 -0
- package/Tests/Utils/AiRemediation/Resource/DatabaseQueryRedactor.test.ts +834 -0
- package/Tests/Utils/AiRemediation/Resource/DockerCliGrammar.test.ts +747 -0
- package/Tests/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.test.ts +1259 -0
- package/Tests/Utils/AiRemediation/Resource/DockerOutputRedaction.test.ts +386 -0
- package/Tests/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.test.ts +782 -0
- package/Tests/Utils/AiRemediation/Resource/GovcCommandPolicy.test.ts +2259 -0
- package/Tests/Utils/AiRemediation/Resource/HostCommandPolicy.test.ts +2019 -0
- package/Tests/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.test.ts +2497 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicy.test.ts +1305 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.test.ts +585 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactor.test.ts +334 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactorCopyParity.test.ts +98 -0
- package/Tests/Utils/AiRemediation/Resource/ResourcePolicyImportClosure.test.ts +391 -0
- package/Tests/Utils/AiRemediation/ResourceAiAgentPolicyCopyParity.test.ts +497 -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/Tests/Utils/SessionReplay/SessionReplayBudgetMetricType.test.ts +383 -0
- package/Types/AI/AIAgentRunLimits.ts +146 -0
- package/Types/AI/ResourceAiAccessApi.ts +140 -0
- package/Types/AI/ResourceAiAccessPermissions.ts +126 -0
- package/Types/AutoRemediation/AiRemediationCommandPlan.ts +183 -2
- 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/Monitor/Recommendation/MonitorRecommendationCatalog.ts +55 -22
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +35 -3
- package/Types/Monitor/RumAlertTemplates.ts +351 -4
- package/Types/Permission.ts +310 -0
- package/Types/ResourceAiAgent/AiResourceType.ts +310 -0
- package/Types/ResourceAiAgent/ResourceAiAccess.ts +580 -0
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +12 -0
- package/Types/Rules/RuleRun.ts +4 -0
- package/Types/Rum/SessionReplayBudgetMetricType.ts +47 -0
- package/Types/Runbook/RunbookStepType.ts +29 -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/Telemetry/TelemetryIngestSurface.ts +14 -2
- 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/AI/InvestigationReport.ts +121 -15
- package/Utils/AiRemediation/Resource/CephCommandPolicy.ts +1936 -0
- package/Utils/AiRemediation/Resource/DatabaseCommandPolicy.ts +166 -0
- package/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.ts +1532 -0
- package/Utils/AiRemediation/Resource/DatabaseQueryRedactor.ts +1394 -0
- package/Utils/AiRemediation/Resource/DockerCliGrammar.ts +1839 -0
- package/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.ts +490 -0
- package/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.ts +687 -0
- package/Utils/AiRemediation/Resource/GovcCommandPolicy.ts +2096 -0
- package/Utils/AiRemediation/Resource/HostCommandPolicy.ts +2982 -0
- package/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.ts +1679 -0
- package/Utils/AiRemediation/Resource/ResourceCommandPolicy.ts +851 -0
- package/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.ts +593 -0
- package/Utils/AiRemediation/Resource/ResourceOutputRedactor.ts +2812 -0
- 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/SessionReplay/SessionReplayBudgetMetricType.ts +184 -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/AutoRemediationSuggestion.js +62 -0
- package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js +231 -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 +231 -0
- package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +231 -0
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +231 -0
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.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 +231 -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 +16 -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 +231 -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 +231 -0
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ResourceAiAgent.js +439 -0
- package/build/dist/Models/DatabaseModels/ResourceAiAgent.js.map +1 -0
- package/build/dist/Models/DatabaseModels/RumApplication.js +12 -0
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunnerJob.js +145 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.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 +231 -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/AutoRemediationAPI.js +231 -1
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
- 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/ResourceAiAccessAPI.js +1119 -0
- package/build/dist/Server/API/ResourceAiAccessAPI.js.map +1 -0
- 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/1796300000000-AddResourceAiAgents.js +168 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js.map +1 -0
- 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 +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Semaphore.js +6 -0
- package/build/dist/Server/Infrastructure/Semaphore.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/TelemetryIngest.js +12 -5
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- 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/AnalyticsDatabaseService.js +26 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +601 -3
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +104 -0
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseServerService.js +99 -0
- package/build/dist/Server/Services/DatabaseServerService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +104 -0
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +104 -0
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +114 -2
- package/build/dist/Server/Services/HostService.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/MetricRecordingRuleService.js +46 -0
- package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +25 -4
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +104 -0
- package/build/dist/Server/Services/PodmanHostService.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/ProxmoxClusterService.js +104 -0
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/ResourceAiAccessService.js +1028 -0
- package/build/dist/Server/Services/ResourceAiAccessService.js.map +1 -0
- package/build/dist/Server/Services/ResourceAiAgentJobService.js +173 -0
- package/build/dist/Server/Services/ResourceAiAgentJobService.js.map +1 -0
- package/build/dist/Server/Services/ResourceAiAgentService.js +1687 -0
- package/build/dist/Server/Services/ResourceAiAgentService.js.map +1 -0
- package/build/dist/Server/Services/RunnerJobService.js +344 -5
- package/build/dist/Server/Services/RunnerJobService.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/TelemetryUsageBillingService.js +26 -0
- package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
- package/build/dist/Server/Services/TraceRecordingRuleService.js +37 -0
- package/build/dist/Server/Services/TraceRecordingRuleService.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/VMwareVCenterService.js +104 -0
- package/build/dist/Server/Services/VMwareVCenterService.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 +1036 -32
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +661 -41
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +21 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +354 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js +160 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js +33 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js +640 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js +226 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +602 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +90 -14
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +73 -10
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +73 -10
- 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/AutoRemediation/CommandPlanExecutor.js +290 -13
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +29 -1
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +9 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.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/SessionReplay/SessionReplayBudgetMetrics.js +627 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js +60 -5
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js.map +1 -1
- 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/Alert.js +19 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +19 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +17 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +186 -210
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +22 -16
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +233 -167
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +273 -111
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js +129 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js +266 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js +258 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js +104 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js +183 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js +123 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js.map +1 -0
- package/build/dist/Types/AI/AIAgentRunLimits.js +91 -0
- package/build/dist/Types/AI/AIAgentRunLimits.js.map +1 -0
- package/build/dist/Types/AI/ResourceAiAccessApi.js +30 -0
- package/build/dist/Types/AI/ResourceAiAccessApi.js.map +1 -0
- package/build/dist/Types/AI/ResourceAiAccessPermissions.js +98 -0
- package/build/dist/Types/AI/ResourceAiAccessPermissions.js.map +1 -0
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +116 -29
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
- 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/Monitor/Recommendation/MonitorRecommendationCatalog.js +47 -21
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +5 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
- package/build/dist/Types/Monitor/RumAlertTemplates.js +199 -3
- package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -1
- package/build/dist/Types/Permission.js +274 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/ResourceAiAgent/AiResourceType.js +242 -0
- package/build/dist/Types/ResourceAiAgent/AiResourceType.js.map +1 -0
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +281 -0
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -0
- 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/Rum/SessionReplayBudgetMetricType.js +45 -0
- package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepType.js +27 -0
- package/build/dist/Types/Runbook/RunbookStepType.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/Telemetry/TelemetryIngestSurface.js +13 -2
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.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/AI/InvestigationReport.js +71 -11
- package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
- package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js +1462 -0
- package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js +122 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js +1139 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js +1060 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js +1301 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js +370 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js +498 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js +1565 -0
- package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js +2206 -0
- package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js +1129 -0
- package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js +565 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js +408 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js +1910 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js.map +1 -0
- 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/SessionReplay/SessionReplayBudgetMetricType.js +160 -0
- package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.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,2778 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_MESSAGE_QUEUE_GUIDE_SYSTEM,
|
|
7
|
+
MESSAGE_QUEUE_DOCS_PATH,
|
|
8
|
+
MESSAGE_QUEUE_GUIDE_MIN_SPANS,
|
|
9
|
+
MESSAGE_QUEUE_MIN_SPANS_ENV_NAME,
|
|
10
|
+
MESSAGE_QUEUE_SETUP_GUIDE_OPTIONS,
|
|
11
|
+
MESSAGE_QUEUE_SUPPORTED_SYSTEMS_ANCHOR,
|
|
12
|
+
MessageQueueChartedMetricRow,
|
|
13
|
+
MessageQueueDocumentationTarget,
|
|
14
|
+
getAwsRegionFromBrokerAddress,
|
|
15
|
+
getAzureMonitorCollectedMetricNames,
|
|
16
|
+
getMessageQueueChartedMetricRows,
|
|
17
|
+
getMessageQueueDestinationAttributeKeys,
|
|
18
|
+
getMessageQueueDocsUrl,
|
|
19
|
+
getMessageQueueGuideReceivers,
|
|
20
|
+
getMessageQueueGuideSystems,
|
|
21
|
+
getMessageQueueMetricTypeLabel,
|
|
22
|
+
getMessageQueueSetupGuide,
|
|
23
|
+
getMessagingInstrumentationNotes,
|
|
24
|
+
markdownInlineCode,
|
|
25
|
+
parseMessageQueueBrokerAddress,
|
|
26
|
+
resolveMessageQueueGuideSystem,
|
|
27
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Pages/MessageQueue/Utils/DocumentationMarkdown";
|
|
28
|
+
import {
|
|
29
|
+
SETUP_GUIDE_API_KEY_PLACEHOLDER,
|
|
30
|
+
SETUP_GUIDE_URL_PLACEHOLDER,
|
|
31
|
+
SetupGuideContent,
|
|
32
|
+
SetupGuideLink,
|
|
33
|
+
SetupGuideOption,
|
|
34
|
+
SetupGuideStep,
|
|
35
|
+
SetupGuideTopic,
|
|
36
|
+
getSetupGuideCodeBlocks,
|
|
37
|
+
getSetupGuideMarkdown,
|
|
38
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Components/SetupGuide/SetupGuide";
|
|
39
|
+
import {
|
|
40
|
+
MESSAGE_QUEUE_DISCOVERY_CLOUD_METRIC_WINDOW_MINUTES,
|
|
41
|
+
MESSAGE_QUEUE_DISCOVERY_INTERVAL_MINUTES,
|
|
42
|
+
MESSAGE_QUEUE_DISCOVERY_WINDOW_MINUTES,
|
|
43
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Components/MessageQueue/MessageQueueOverviewPresentation";
|
|
44
|
+
import {
|
|
45
|
+
canBrokerMetricsReachMessageQueue,
|
|
46
|
+
getMessageQueueSystemLabel,
|
|
47
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Pages/MessageQueue/Utils/MessageQueuePresentation";
|
|
48
|
+
import {
|
|
49
|
+
MESSAGING_SYSTEMS,
|
|
50
|
+
MessagingBrokerMetricsSource,
|
|
51
|
+
MessagingSystemDescriptor,
|
|
52
|
+
getMessagingBrokerMetricsReceivers,
|
|
53
|
+
getMessagingBrokerMetricsSource,
|
|
54
|
+
} from "../../../Types/MessageQueue/MessagingSystem";
|
|
55
|
+
import {
|
|
56
|
+
MessageQueueMetricDescriptor,
|
|
57
|
+
getMessageQueueMetricsForSystem,
|
|
58
|
+
} from "../../../Types/MessageQueue/MessageQueueMetricCatalog";
|
|
59
|
+
import {
|
|
60
|
+
DEFAULT_MESSAGE_QUEUE_MIN_SPANS,
|
|
61
|
+
MESSAGE_QUEUE_MIN_SPANS_ENV,
|
|
62
|
+
} from "../../../Server/Utils/Telemetry/MessageQueueDiscovery";
|
|
63
|
+
import AggregationType from "../../../Types/BaseDatabase/AggregationType";
|
|
64
|
+
import slugify from "../../../Server/Types/MarkdownSlugify";
|
|
65
|
+
|
|
66
|
+
/*
|
|
67
|
+
* The in-app Queues setup guide, on the shared SetupGuide layout: the
|
|
68
|
+
* ingestion key as step 1, then instrument the applications, send the
|
|
69
|
+
* broker's metrics (or report BullMQ's depth) and check it worked, with the
|
|
70
|
+
* identity, the charted metrics and JMS's ActiveMQ scraper under Advanced
|
|
71
|
+
* and the docs page's troubleshooting entries under Troubleshooting.
|
|
72
|
+
*
|
|
73
|
+
* A collector config that does not parse, or that drifted from the one the
|
|
74
|
+
* docs page validated, installs a collector that silently sends nothing —
|
|
75
|
+
* so these tests hold every block the guide emits to the docs page's own
|
|
76
|
+
* blocks (each validated with `otelcol-contrib validate` on
|
|
77
|
+
* otel/opentelemetry-collector-contrib 0.161.0), check every YAML block
|
|
78
|
+
* parses and exports to the viewer's endpoint with the viewer's key, and
|
|
79
|
+
* pin the catalog facts the guide states: receivers, Prometheus ports and
|
|
80
|
+
* paths, the Azure and Pub/Sub metric lists, the charted metrics table, how
|
|
81
|
+
* a queue's own telemetry finds it, the numbers discovery runs on and the
|
|
82
|
+
* docs page's troubleshooting.
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
const REPO_PACKAGES: string = path.join(__dirname, "..", "..", "..", "..");
|
|
86
|
+
const QUEUES_DOC: string = fs.readFileSync(
|
|
87
|
+
path.join(
|
|
88
|
+
REPO_PACKAGES,
|
|
89
|
+
"App",
|
|
90
|
+
"FeatureSet",
|
|
91
|
+
"Docs",
|
|
92
|
+
"Content",
|
|
93
|
+
"en",
|
|
94
|
+
"telemetry",
|
|
95
|
+
"queues.md",
|
|
96
|
+
),
|
|
97
|
+
"utf8",
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
interface GuideVariables {
|
|
101
|
+
oneuptimeUrl: string;
|
|
102
|
+
apiKey: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// The values the docs page writes where the in-app guide interpolates.
|
|
106
|
+
const DOCS_VARS: GuideVariables = {
|
|
107
|
+
oneuptimeUrl: "https://oneuptime.com",
|
|
108
|
+
apiKey: "YOUR_TELEMETRY_INGESTION_TOKEN",
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const VARS: GuideVariables = {
|
|
112
|
+
oneuptimeUrl: "https://oneuptime.example.com",
|
|
113
|
+
apiKey: "ingest-key-123",
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
type YamlMap = { [key: string]: any };
|
|
117
|
+
|
|
118
|
+
interface CodeBlock {
|
|
119
|
+
language: string;
|
|
120
|
+
body: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* Fenced code blocks of some markdown, with their info string. */
|
|
124
|
+
function codeBlocks(markdown: string): Array<CodeBlock> {
|
|
125
|
+
const blocks: Array<CodeBlock> = [];
|
|
126
|
+
let current: { language: string; lines: Array<string> } | null = null;
|
|
127
|
+
|
|
128
|
+
for (const line of markdown.split("\n")) {
|
|
129
|
+
const fence: RegExpMatchArray | null = line.match(/^```(\S*)\s*$/);
|
|
130
|
+
if (fence) {
|
|
131
|
+
if (current) {
|
|
132
|
+
blocks.push({
|
|
133
|
+
language: current.language,
|
|
134
|
+
body: current.lines.join("\n"),
|
|
135
|
+
});
|
|
136
|
+
current = null;
|
|
137
|
+
} else {
|
|
138
|
+
current = { language: fence[1] || "", lines: [] };
|
|
139
|
+
}
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (current) {
|
|
143
|
+
current.lines.push(line);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
expect(current).toBeNull();
|
|
148
|
+
return blocks;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function yamlBlocks(markdown: string): Array<YamlMap> {
|
|
152
|
+
return codeBlocks(markdown)
|
|
153
|
+
.filter((block: CodeBlock): boolean => {
|
|
154
|
+
return block.language === "yaml";
|
|
155
|
+
})
|
|
156
|
+
.map((block: CodeBlock): YamlMap => {
|
|
157
|
+
return yaml.load(block.body) as YamlMap;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Markdown without its code blocks: the prose a reader reads.
|
|
162
|
+
function proseOf(markdown: string): string {
|
|
163
|
+
return markdown.replace(/```[^\n]*\n[\s\S]*?```/g, "");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* A section of the docs page: its heading down to the next heading of the same level or above. */
|
|
167
|
+
function docsSectionAt(heading: string): string {
|
|
168
|
+
const level: number = heading.indexOf(" ");
|
|
169
|
+
const marker: string = `\n${heading}\n`;
|
|
170
|
+
const start: number = QUEUES_DOC.indexOf(marker);
|
|
171
|
+
expect({ heading, found: start > -1 }).toEqual({ heading, found: true });
|
|
172
|
+
const rest: string = QUEUES_DOC.slice(start + marker.length);
|
|
173
|
+
const end: number = rest.search(new RegExp(`\\n#{2,${level}} `));
|
|
174
|
+
return end === -1 ? rest : rest.slice(0, end);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* A system's broker-metrics section on the docs page: its "### <name>" heading down to the next heading. */
|
|
178
|
+
function docsSection(displayName: string): string {
|
|
179
|
+
return docsSectionAt(`### ${displayName}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const DOCS_SLUGS: Set<string> = ((): Set<string> => {
|
|
183
|
+
const slugs: Set<string> = new Set<string>();
|
|
184
|
+
for (const line of QUEUES_DOC.split("\n")) {
|
|
185
|
+
const heading: RegExpMatchArray | null = line.match(/^#{1,6}\s+(.+?)\s*$/);
|
|
186
|
+
if (heading) {
|
|
187
|
+
slugs.add(slugify(heading[1]!));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return slugs;
|
|
191
|
+
})();
|
|
192
|
+
|
|
193
|
+
/*
|
|
194
|
+
* The docs page and the guide quote shell values differently (the guide
|
|
195
|
+
* quotes everything that comes from data); compare the words, not the
|
|
196
|
+
* quoting.
|
|
197
|
+
*/
|
|
198
|
+
function normalizeShell(body: string): string {
|
|
199
|
+
return body
|
|
200
|
+
.split("\n")
|
|
201
|
+
.map((line: string): string => {
|
|
202
|
+
return line
|
|
203
|
+
.replace(/^(export [A-Z_]+=)"(.*)"$/, "$1$2")
|
|
204
|
+
.replace(/\s+$/, "");
|
|
205
|
+
})
|
|
206
|
+
.join("\n")
|
|
207
|
+
.trim();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// The SDK environment block every guide shares; not a per-system block.
|
|
211
|
+
function isSdkEnvironmentBlock(block: CodeBlock): boolean {
|
|
212
|
+
return block.body.startsWith("OTEL_EXPORTER_OTLP_ENDPOINT=");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function descriptorOf(system: string): MessagingSystemDescriptor {
|
|
216
|
+
const descriptor: MessagingSystemDescriptor | undefined =
|
|
217
|
+
MESSAGING_SYSTEMS.find((candidate: MessagingSystemDescriptor): boolean => {
|
|
218
|
+
return candidate.system === system;
|
|
219
|
+
});
|
|
220
|
+
expect(descriptor).toBeDefined();
|
|
221
|
+
return descriptor!;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function productContent(
|
|
225
|
+
system: string,
|
|
226
|
+
vars: GuideVariables = VARS,
|
|
227
|
+
): SetupGuideContent {
|
|
228
|
+
return getMessageQueueSetupGuide({
|
|
229
|
+
oneuptimeUrl: vars.oneuptimeUrl,
|
|
230
|
+
apiKey: vars.apiKey,
|
|
231
|
+
system: system,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function queueContent(
|
|
236
|
+
target: MessageQueueDocumentationTarget,
|
|
237
|
+
vars: GuideVariables = VARS,
|
|
238
|
+
): SetupGuideContent {
|
|
239
|
+
return getMessageQueueSetupGuide({
|
|
240
|
+
oneuptimeUrl: vars.oneuptimeUrl,
|
|
241
|
+
apiKey: vars.apiKey,
|
|
242
|
+
system: target.system,
|
|
243
|
+
queue: target,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// The whole product guide as one markdown document, as the reader reads it.
|
|
248
|
+
function productGuide(system: string, vars: GuideVariables = VARS): string {
|
|
249
|
+
return getSetupGuideMarkdown(productContent(system, vars));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function queueGuide(
|
|
253
|
+
target: MessageQueueDocumentationTarget,
|
|
254
|
+
vars: GuideVariables = VARS,
|
|
255
|
+
): string {
|
|
256
|
+
return getSetupGuideMarkdown(queueContent(target, vars));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function stepTitles(content: SetupGuideContent): Array<string> {
|
|
260
|
+
return content.steps.map((step: SetupGuideStep): string => {
|
|
261
|
+
return step.title;
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function topicTitles(
|
|
266
|
+
topics: Array<SetupGuideTopic> | undefined,
|
|
267
|
+
): Array<string> {
|
|
268
|
+
return (topics || []).map((topic: SetupGuideTopic): string => {
|
|
269
|
+
return topic.title;
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function findTopic(
|
|
274
|
+
topics: Array<SetupGuideTopic> | undefined,
|
|
275
|
+
title: string,
|
|
276
|
+
): SetupGuideTopic {
|
|
277
|
+
const topic: SetupGuideTopic | undefined = (topics || []).find(
|
|
278
|
+
(candidate: SetupGuideTopic): boolean => {
|
|
279
|
+
return candidate.title === title;
|
|
280
|
+
},
|
|
281
|
+
);
|
|
282
|
+
expect({ title, found: Boolean(topic) }).toEqual({ title, found: true });
|
|
283
|
+
return topic!;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function findStep(content: SetupGuideContent, title: string): SetupGuideStep {
|
|
287
|
+
const step: SetupGuideStep | undefined = content.steps.find(
|
|
288
|
+
(candidate: SetupGuideStep): boolean => {
|
|
289
|
+
return candidate.title === title;
|
|
290
|
+
},
|
|
291
|
+
);
|
|
292
|
+
expect({ title, found: Boolean(step) }).toEqual({ title, found: true });
|
|
293
|
+
return step!;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const SYSTEMS: Array<string> = MESSAGING_SYSTEMS.map(
|
|
297
|
+
(descriptor: MessagingSystemDescriptor): string => {
|
|
298
|
+
return descriptor.system;
|
|
299
|
+
},
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
/* A realistic queue of every catalog system, as its Documentation tab sees it. */
|
|
303
|
+
function sampleQueue(system: string): MessageQueueDocumentationTarget {
|
|
304
|
+
const descriptor: MessagingSystemDescriptor = descriptorOf(system);
|
|
305
|
+
return {
|
|
306
|
+
system,
|
|
307
|
+
destination: "orders",
|
|
308
|
+
brokerScope:
|
|
309
|
+
descriptor.brokerScope === "azure-namespace" ? "shop-prod" : "",
|
|
310
|
+
brokerAddress: "broker-1.internal:9092",
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// A system OneUptime does not know, as its spans name it.
|
|
315
|
+
const LONG_TAIL_QUEUE: MessageQueueDocumentationTarget = {
|
|
316
|
+
system: "ibmmq",
|
|
317
|
+
destination: "DEV.QUEUE.1",
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
/*
|
|
321
|
+
* Every guide the dashboard can show: each catalog system's product guide,
|
|
322
|
+
* a prefilled queue of each, and a long-tail system's product and queue
|
|
323
|
+
* guide.
|
|
324
|
+
*/
|
|
325
|
+
const ALL_GUIDES: Array<[string, SetupGuideContent]> = [
|
|
326
|
+
...SYSTEMS.flatMap((system: string): Array<[string, SetupGuideContent]> => {
|
|
327
|
+
return [
|
|
328
|
+
[`${system} (product)`, productContent(system)],
|
|
329
|
+
[`${system} (queue)`, queueContent(sampleQueue(system))],
|
|
330
|
+
];
|
|
331
|
+
}),
|
|
332
|
+
["ibmmq (product)", productContent("ibmmq")],
|
|
333
|
+
["ibmmq (queue)", queueContent(LONG_TAIL_QUEUE)],
|
|
334
|
+
];
|
|
335
|
+
|
|
336
|
+
describe("every guide's YAML parses and exports to the viewer's OneUptime", () => {
|
|
337
|
+
test.each(ALL_GUIDES)("%s", (_name: string, content: SetupGuideContent) => {
|
|
338
|
+
const markdown: string = getSetupGuideMarkdown(content);
|
|
339
|
+
for (const config of yamlBlocks(markdown)) {
|
|
340
|
+
expect(config["exporters"]).toEqual({
|
|
341
|
+
otlphttp: {
|
|
342
|
+
endpoint: `${VARS.oneuptimeUrl}/otlp`,
|
|
343
|
+
headers: { "x-oneuptime-token": VARS.apiKey },
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const pipelines: YamlMap = config["service"]["pipelines"];
|
|
348
|
+
expect(Object.keys(pipelines).length).toBeGreaterThan(0);
|
|
349
|
+
|
|
350
|
+
for (const pipeline of Object.values(pipelines) as Array<YamlMap>) {
|
|
351
|
+
expect(pipeline["exporters"]).toEqual(["otlphttp"]);
|
|
352
|
+
for (const receiver of pipeline["receivers"]) {
|
|
353
|
+
expect(Object.keys(config["receivers"])).toContain(receiver);
|
|
354
|
+
}
|
|
355
|
+
for (const processor of pipeline["processors"]) {
|
|
356
|
+
expect(Object.keys(config["processors"])).toContain(processor);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
for (const extension of config["service"]["extensions"] || []) {
|
|
361
|
+
expect(Object.keys(config["extensions"])).toContain(extension);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// The SDK environment points at the same place with the same key.
|
|
366
|
+
const environment: CodeBlock | undefined = codeBlocks(markdown).find(
|
|
367
|
+
isSdkEnvironmentBlock,
|
|
368
|
+
);
|
|
369
|
+
if (environment) {
|
|
370
|
+
expect(environment.body).toContain(
|
|
371
|
+
`OTEL_EXPORTER_OTLP_ENDPOINT="${VARS.oneuptimeUrl}/otlp"`,
|
|
372
|
+
);
|
|
373
|
+
expect(environment.body).toContain(
|
|
374
|
+
`OTEL_EXPORTER_OTLP_HEADERS="x-oneuptime-token=${VARS.apiKey}"`,
|
|
375
|
+
);
|
|
376
|
+
expect(environment.body).toContain(
|
|
377
|
+
'OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"',
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Never a placeholder the viewer's own values should have replaced.
|
|
382
|
+
expect(markdown).not.toContain(SETUP_GUIDE_API_KEY_PLACEHOLDER);
|
|
383
|
+
expect(markdown).not.toContain(SETUP_GUIDE_URL_PLACEHOLDER);
|
|
384
|
+
expect(markdown).not.toContain("YOUR_TELEMETRY_INGESTION_TOKEN");
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
test.each(ALL_GUIDES)(
|
|
388
|
+
"%s: the blocks a reader copies are the ones read here",
|
|
389
|
+
(_name: string, content: SetupGuideContent) => {
|
|
390
|
+
expect(
|
|
391
|
+
getSetupGuideCodeBlocks(content).map((body: string): string => {
|
|
392
|
+
return body.replace(/\n$/, "");
|
|
393
|
+
}),
|
|
394
|
+
).toEqual(
|
|
395
|
+
codeBlocks(getSetupGuideMarkdown(content)).map(
|
|
396
|
+
(block: CodeBlock): string => {
|
|
397
|
+
return block.body;
|
|
398
|
+
},
|
|
399
|
+
),
|
|
400
|
+
);
|
|
401
|
+
},
|
|
402
|
+
);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
describe("every block equals the docs page's validated one", () => {
|
|
406
|
+
test.each(SYSTEMS)("%s", (system: string) => {
|
|
407
|
+
const descriptor: MessagingSystemDescriptor = descriptorOf(system);
|
|
408
|
+
const content: SetupGuideContent = productContent(system, DOCS_VARS);
|
|
409
|
+
/*
|
|
410
|
+
* JMS's broker path is ActiveMQ's (its JMS section links there), so its
|
|
411
|
+
* blocks are ActiveMQ's.
|
|
412
|
+
*/
|
|
413
|
+
const sections: Array<string> = [docsSection(descriptor.displayName)];
|
|
414
|
+
if (system === "jms") {
|
|
415
|
+
sections.push(docsSection(descriptorOf("activemq").displayName));
|
|
416
|
+
}
|
|
417
|
+
const docsBlocks: Array<CodeBlock> = sections.flatMap(
|
|
418
|
+
(section: string): Array<CodeBlock> => {
|
|
419
|
+
return codeBlocks(section);
|
|
420
|
+
},
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
const guideBlocks: Array<CodeBlock> = codeBlocks(
|
|
424
|
+
getSetupGuideMarkdown(content),
|
|
425
|
+
).filter((block: CodeBlock): boolean => {
|
|
426
|
+
return !isSdkEnvironmentBlock(block);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
for (const block of guideBlocks) {
|
|
430
|
+
const match: CodeBlock | undefined = docsBlocks.find(
|
|
431
|
+
(candidate: CodeBlock): boolean => {
|
|
432
|
+
if (candidate.language !== block.language) {
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
if (block.language === "yaml") {
|
|
436
|
+
return (
|
|
437
|
+
JSON.stringify(yaml.load(candidate.body)) ===
|
|
438
|
+
JSON.stringify(yaml.load(block.body))
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
return normalizeShell(candidate.body) === normalizeShell(block.body);
|
|
442
|
+
},
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
expect({ system, block: block.body, matched: Boolean(match) }).toEqual({
|
|
446
|
+
system,
|
|
447
|
+
block: block.body,
|
|
448
|
+
matched: true,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
test("every system with a ready-made metrics path gets its config, none the fallback", () => {
|
|
454
|
+
for (const system of SYSTEMS) {
|
|
455
|
+
const source: MessagingBrokerMetricsSource =
|
|
456
|
+
descriptorOf(system).brokerMetrics;
|
|
457
|
+
for (const markdown of [
|
|
458
|
+
productGuide(system),
|
|
459
|
+
queueGuide(sampleQueue(system)),
|
|
460
|
+
]) {
|
|
461
|
+
expect(markdown).not.toContain("for the collector configuration.");
|
|
462
|
+
const blocks: Array<CodeBlock> = codeBlocks(markdown).filter(
|
|
463
|
+
(block: CodeBlock): boolean => {
|
|
464
|
+
return !isSdkEnvironmentBlock(block);
|
|
465
|
+
},
|
|
466
|
+
);
|
|
467
|
+
if (source.kind === "none" && system !== "jms" && system !== "bullmq") {
|
|
468
|
+
expect(blocks).toEqual([]);
|
|
469
|
+
} else {
|
|
470
|
+
expect({ system, blocks: blocks.length > 0 }).toEqual({
|
|
471
|
+
system,
|
|
472
|
+
blocks: true,
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
describe("the configs run the catalog's receivers", () => {
|
|
481
|
+
test.each(
|
|
482
|
+
SYSTEMS.filter((system: string): boolean => {
|
|
483
|
+
return ["receiver", "prometheus", "cloud-monitoring"].includes(
|
|
484
|
+
descriptorOf(system).brokerMetrics.kind,
|
|
485
|
+
);
|
|
486
|
+
}),
|
|
487
|
+
)("%s", (system: string) => {
|
|
488
|
+
const source: MessagingBrokerMetricsSource =
|
|
489
|
+
descriptorOf(system).brokerMetrics;
|
|
490
|
+
const content: SetupGuideContent = productContent(system);
|
|
491
|
+
const configs: Array<YamlMap> = yamlBlocks(getSetupGuideMarkdown(content));
|
|
492
|
+
expect(configs).toHaveLength(1);
|
|
493
|
+
// The config is the broker step's.
|
|
494
|
+
expect(
|
|
495
|
+
yamlBlocks(findStep(content, "Send the broker's metrics").markdown || ""),
|
|
496
|
+
).toEqual(configs);
|
|
497
|
+
|
|
498
|
+
const receiverTypes: Array<string> = Object.keys(
|
|
499
|
+
configs[0]!["receivers"],
|
|
500
|
+
).map((id: string): string => {
|
|
501
|
+
return id.split("/")[0]!;
|
|
502
|
+
});
|
|
503
|
+
const expected: string =
|
|
504
|
+
source.kind === "prometheus"
|
|
505
|
+
? "prometheus"
|
|
506
|
+
: (source as { receiver: string }).receiver;
|
|
507
|
+
|
|
508
|
+
expect(receiverTypes).toEqual([expected]);
|
|
509
|
+
expect(getMessageQueueGuideReceivers(system)).toContain(expected);
|
|
510
|
+
expect(getMessageQueueGuideReceivers(system)).toEqual(
|
|
511
|
+
getMessagingBrokerMetricsReceivers(source),
|
|
512
|
+
);
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
test("every Prometheus scrape uses the catalog's port and path", () => {
|
|
516
|
+
for (const system of SYSTEMS) {
|
|
517
|
+
const source: MessagingBrokerMetricsSource =
|
|
518
|
+
descriptorOf(system).brokerMetrics;
|
|
519
|
+
if (source.kind !== "prometheus") {
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
const config: YamlMap = yamlBlocks(productGuide(system))[0]!;
|
|
523
|
+
const scrapes: Array<YamlMap> =
|
|
524
|
+
config["receivers"]["prometheus"]["config"]["scrape_configs"];
|
|
525
|
+
expect(scrapes).toHaveLength(1);
|
|
526
|
+
expect(scrapes[0]!["metrics_path"]).toBe(source.path);
|
|
527
|
+
for (const target of scrapes[0]!["static_configs"][0]["targets"]) {
|
|
528
|
+
expect(target.endsWith(`:${source.port}`)).toBe(true);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
test("Pulsar's scrape keeps exactly the charted families", () => {
|
|
534
|
+
const config: YamlMap = yamlBlocks(productGuide("pulsar"))[0]!;
|
|
535
|
+
const relabel: YamlMap =
|
|
536
|
+
config["receivers"]["prometheus"]["config"]["scrape_configs"][0][
|
|
537
|
+
"metric_relabel_configs"
|
|
538
|
+
][0];
|
|
539
|
+
expect(relabel["action"]).toBe("keep");
|
|
540
|
+
expect(relabel["source_labels"]).toEqual(["__name__"]);
|
|
541
|
+
// Prometheus anchors a relabel regex at both ends.
|
|
542
|
+
const keep: RegExp = new RegExp(`^(?:${relabel["regex"]})$`);
|
|
543
|
+
for (const descriptor of getMessageQueueMetricsForSystem("pulsar")) {
|
|
544
|
+
expect(keep.test(descriptor.metricName)).toBe(true);
|
|
545
|
+
}
|
|
546
|
+
expect(keep.test("pulsar_throughput_in")).toBe(false);
|
|
547
|
+
expect(keep.test("pulsar_msg_backlog_extra")).toBe(false);
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
test.each(["servicebus", "eventhubs"])(
|
|
551
|
+
"%s's Azure Monitor config collects every charted metric",
|
|
552
|
+
(system: string) => {
|
|
553
|
+
const collected: Array<string> =
|
|
554
|
+
getAzureMonitorCollectedMetricNames(system);
|
|
555
|
+
for (const descriptor of getMessageQueueMetricsForSystem(system)) {
|
|
556
|
+
expect(collected).toContain(descriptor.metricName);
|
|
557
|
+
}
|
|
558
|
+
expect(collected.length).toBe(
|
|
559
|
+
getMessageQueueMetricsForSystem(system).length,
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
const config: YamlMap = yamlBlocks(productGuide(system))[0]!;
|
|
563
|
+
const receiver: YamlMap = config["receivers"][`azure_monitor/${system}`];
|
|
564
|
+
const [resourceType] = receiver["services"];
|
|
565
|
+
expect(Object.keys(receiver["metrics"])).toEqual([resourceType]);
|
|
566
|
+
expect(
|
|
567
|
+
Object.entries(receiver["metrics"][resourceType]).map(
|
|
568
|
+
([name, aggregations]: [string, any]): string => {
|
|
569
|
+
return `azure_${name}_${aggregations[0]}`.toLowerCase();
|
|
570
|
+
},
|
|
571
|
+
),
|
|
572
|
+
).toEqual(collected);
|
|
573
|
+
},
|
|
574
|
+
);
|
|
575
|
+
|
|
576
|
+
test("the Pub/Sub config lists exactly the charted metric types", () => {
|
|
577
|
+
const config: YamlMap = yamlBlocks(productGuide("gcp_pubsub"))[0]!;
|
|
578
|
+
expect(
|
|
579
|
+
config["receivers"]["googlecloudmonitoring"]["metrics_list"].map(
|
|
580
|
+
(entry: YamlMap): string => {
|
|
581
|
+
return entry["metric_name"];
|
|
582
|
+
},
|
|
583
|
+
),
|
|
584
|
+
).toEqual(
|
|
585
|
+
getMessageQueueMetricsForSystem("gcp_pubsub").map(
|
|
586
|
+
(descriptor: MessageQueueMetricDescriptor): string => {
|
|
587
|
+
return descriptor.metricName;
|
|
588
|
+
},
|
|
589
|
+
),
|
|
590
|
+
);
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
test("the ActiveMQ scraper targets ActiveMQ and pushes to the viewer's OneUptime", () => {
|
|
594
|
+
const content: SetupGuideContent = productContent("activemq");
|
|
595
|
+
const markdown: string = getSetupGuideMarkdown(content);
|
|
596
|
+
const scraper: CodeBlock | undefined = codeBlocks(
|
|
597
|
+
findStep(content, "Send the broker's metrics").markdown || "",
|
|
598
|
+
).find((block: CodeBlock): boolean => {
|
|
599
|
+
return block.body.includes("java -jar opentelemetry-jmx-scraper.jar");
|
|
600
|
+
});
|
|
601
|
+
expect(scraper).toBeDefined();
|
|
602
|
+
expect(scraper!.body).toContain("export OTEL_JMX_TARGET_SYSTEM=activemq");
|
|
603
|
+
expect(scraper!.body).toContain(
|
|
604
|
+
`export OTEL_EXPORTER_OTLP_ENDPOINT="${VARS.oneuptimeUrl}/otlp"`,
|
|
605
|
+
);
|
|
606
|
+
expect(scraper!.body).toContain(
|
|
607
|
+
`export OTEL_EXPORTER_OTLP_HEADERS="x-oneuptime-token=${VARS.apiKey}"`,
|
|
608
|
+
);
|
|
609
|
+
expect(markdown).toContain(
|
|
610
|
+
(descriptorOf("activemq").brokerMetrics as { scraper: string }).scraper,
|
|
611
|
+
);
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
/*
|
|
616
|
+
* How late the guide's `aws_cloudwatch` receiver delivers. It asks
|
|
617
|
+
* CloudWatch, once per `collection_interval`, for the whole periods that
|
|
618
|
+
* ended by now - `delay`, each point stamped with its period's start
|
|
619
|
+
* (receiver/awscloudwatchreceiver metrics.go @v0.161.0): right after a
|
|
620
|
+
* scrape the newest point is delay + period old, just before the next one
|
|
621
|
+
* delay + 2 × period + collection_interval. The guide states that for the
|
|
622
|
+
* configuration it prints, in the docs page's words, and sends whoever
|
|
623
|
+
* builds a monitor over those metrics to Late metrics under Alerting.
|
|
624
|
+
*/
|
|
625
|
+
describe("how late CloudWatch's metrics arrive", () => {
|
|
626
|
+
// "10m" → 10, "30s" → 0.5.
|
|
627
|
+
function minutesOf(value: unknown): number {
|
|
628
|
+
const match: RegExpMatchArray | null = String(value).match(/^(\d+)(s|m)$/);
|
|
629
|
+
expect({ value, duration: Boolean(match) }).toEqual({
|
|
630
|
+
value,
|
|
631
|
+
duration: true,
|
|
632
|
+
});
|
|
633
|
+
const amount: number = Number(match![1]);
|
|
634
|
+
return match![2] === "m" ? amount : amount / 60;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function delayLine(markdown: string): string {
|
|
638
|
+
const lines: Array<string> = markdown
|
|
639
|
+
.split("\n")
|
|
640
|
+
.filter((line: string): boolean => {
|
|
641
|
+
return line.includes("`delay` waits for CloudWatch");
|
|
642
|
+
});
|
|
643
|
+
expect(lines).toHaveLength(1);
|
|
644
|
+
return lines[0]!;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
test.each(["aws_sqs", "aws.sns"])(
|
|
648
|
+
"%s's guides state it for the configuration they print, as the docs page does",
|
|
649
|
+
(system: string) => {
|
|
650
|
+
const guides: Array<[string, SetupGuideContent]> = [
|
|
651
|
+
["a queue's", productContent(system)],
|
|
652
|
+
["this queue's", queueContent(sampleQueue(system))],
|
|
653
|
+
];
|
|
654
|
+
for (const [whose, content] of guides) {
|
|
655
|
+
const markdown: string = getSetupGuideMarkdown(content);
|
|
656
|
+
const configs: Array<YamlMap> = yamlBlocks(markdown);
|
|
657
|
+
expect(configs).toHaveLength(1);
|
|
658
|
+
const receivers: Array<YamlMap> = Object.entries(
|
|
659
|
+
configs[0]!["receivers"] as YamlMap,
|
|
660
|
+
)
|
|
661
|
+
.filter(([id]: [string, unknown]): boolean => {
|
|
662
|
+
return id.split("/")[0] === "aws_cloudwatch";
|
|
663
|
+
})
|
|
664
|
+
.map(([, receiver]: [string, unknown]): YamlMap => {
|
|
665
|
+
return receiver as YamlMap;
|
|
666
|
+
});
|
|
667
|
+
expect(receivers).toHaveLength(1);
|
|
668
|
+
|
|
669
|
+
const metrics: YamlMap = receivers[0]!["metrics"];
|
|
670
|
+
const period: number = minutesOf(metrics["period"]);
|
|
671
|
+
const delay: number = minutesOf(metrics["delay"]);
|
|
672
|
+
const interval: number = minutesOf(metrics["collection_interval"]);
|
|
673
|
+
const late: string = `${delay + period} to ${
|
|
674
|
+
delay + 2 * period + interval
|
|
675
|
+
} minutes late`;
|
|
676
|
+
|
|
677
|
+
// Said in the broker step, next to the config.
|
|
678
|
+
expect(
|
|
679
|
+
delayLine(findStep(content, "Send the broker's metrics").markdown!),
|
|
680
|
+
).toBe(
|
|
681
|
+
`- \`delay\` waits for CloudWatch to publish a period, so with this configuration ${whose} broker metrics arrive ${late}: a monitor over them needs a longer window (see **Late metrics** under [Alerting](${MESSAGE_QUEUE_DOCS_PATH}#alerting)).`,
|
|
682
|
+
);
|
|
683
|
+
expect(delayLine(markdown)).toContain(late);
|
|
684
|
+
expect(markdown).not.toContain("minutes behind");
|
|
685
|
+
|
|
686
|
+
// Troubleshooting says the same of the same delay.
|
|
687
|
+
expect(
|
|
688
|
+
findTopic(content.troubleshooting, "Broker health stays empty")
|
|
689
|
+
.markdown,
|
|
690
|
+
).toContain(
|
|
691
|
+
`\`aws_cloudwatch\` waits \`delay\` (${delay} minutes) for CloudWatch to publish, so its points arrive ${late}.`,
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
// The docs page says the same of the same configuration.
|
|
695
|
+
expect(delayLine(docsSection("Amazon SQS"))).toContain(
|
|
696
|
+
`so with this configuration a queue's broker metrics arrive ${late}: a monitor over them needs a longer window (see **Late metrics** under [Alerting](#alerting)).`,
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// A queue's check step says when its Broker health fills in.
|
|
701
|
+
const queueCheck: SetupGuideStep = findStep(
|
|
702
|
+
queueContent(sampleQueue(system)),
|
|
703
|
+
"Check that this queue fills in",
|
|
704
|
+
);
|
|
705
|
+
expect(queueCheck.markdown).toContain(
|
|
706
|
+
"with the configuration in step 3, CloudWatch's arrive 11 to 17 minutes late.",
|
|
707
|
+
);
|
|
708
|
+
},
|
|
709
|
+
);
|
|
710
|
+
|
|
711
|
+
test("the Alerting link lands on the section that says how long a window to use", () => {
|
|
712
|
+
const alerting: string = docsSectionAt("## Alerting");
|
|
713
|
+
expect(alerting).toContain("**Late metrics.**");
|
|
714
|
+
expect(alerting).toContain("needs a window longer than that");
|
|
715
|
+
});
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
describe("what a queue's page charts", () => {
|
|
719
|
+
/* The docs section's "| Metric | Shown as | Type |" rows. */
|
|
720
|
+
function docsRows(displayName: string): Array<MessageQueueChartedMetricRow> {
|
|
721
|
+
return docsSection(displayName)
|
|
722
|
+
.split("\n")
|
|
723
|
+
.filter((line: string): boolean => {
|
|
724
|
+
return line.startsWith("| `");
|
|
725
|
+
})
|
|
726
|
+
.map((line: string): MessageQueueChartedMetricRow => {
|
|
727
|
+
const cells: Array<string> = line
|
|
728
|
+
.slice(1, -1)
|
|
729
|
+
.split(" | ")
|
|
730
|
+
.map((cell: string): string => {
|
|
731
|
+
return cell.trim();
|
|
732
|
+
});
|
|
733
|
+
return { metric: cells[0]!, title: cells[1]!, type: cells[2]! };
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
test.each(SYSTEMS)("%s's table equals the docs page's", (system: string) => {
|
|
738
|
+
expect(getMessageQueueChartedMetricRows(system)).toEqual(
|
|
739
|
+
docsRows(descriptorOf(system).displayName),
|
|
740
|
+
);
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
test.each(SYSTEMS)(
|
|
744
|
+
"%s's guide renders that table under Advanced",
|
|
745
|
+
(system: string) => {
|
|
746
|
+
const content: SetupGuideContent = productContent(system);
|
|
747
|
+
const topic: SetupGuideTopic = findTopic(
|
|
748
|
+
content.advanced,
|
|
749
|
+
"What a queue's page charts",
|
|
750
|
+
);
|
|
751
|
+
expect(
|
|
752
|
+
findTopic(
|
|
753
|
+
queueContent(sampleQueue(system)).advanced,
|
|
754
|
+
"What this queue's page charts",
|
|
755
|
+
).markdown.length,
|
|
756
|
+
).toBeGreaterThan(0);
|
|
757
|
+
|
|
758
|
+
const rows: Array<MessageQueueChartedMetricRow> =
|
|
759
|
+
getMessageQueueChartedMetricRows(system);
|
|
760
|
+
for (const row of rows) {
|
|
761
|
+
expect(topic.markdown).toContain(
|
|
762
|
+
`| ${row.metric} | ${row.title} | ${row.type} |`,
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
if (rows.length > 0) {
|
|
766
|
+
// Every row but a counter's: MessageQueueBrokerHealth.test holds it.
|
|
767
|
+
expect(topic.markdown).toContain(
|
|
768
|
+
"under **Broker health**, and each gauge and each count per period has **Create monitor**",
|
|
769
|
+
);
|
|
770
|
+
} else {
|
|
771
|
+
expect(topic.markdown).toContain(
|
|
772
|
+
`OneUptime charts no broker metrics for ${descriptorOf(system).displayName} under **Broker health**`,
|
|
773
|
+
);
|
|
774
|
+
expect(topic.markdown).toContain("**Metrics** explorer");
|
|
775
|
+
expect(topic.markdown).toContain("**Metrics** tab");
|
|
776
|
+
}
|
|
777
|
+
// The table is reference material: never in a step.
|
|
778
|
+
for (const step of content.steps) {
|
|
779
|
+
expect(step.markdown || "").not.toContain("| Metric | Shown as |");
|
|
780
|
+
}
|
|
781
|
+
},
|
|
782
|
+
);
|
|
783
|
+
|
|
784
|
+
test("every charted metric is in its system's table, the JSON-stream twins folded", () => {
|
|
785
|
+
for (const system of SYSTEMS) {
|
|
786
|
+
const text: string = getMessageQueueChartedMetricRows(system)
|
|
787
|
+
.map((row: MessageQueueChartedMetricRow): string => {
|
|
788
|
+
return row.metric;
|
|
789
|
+
})
|
|
790
|
+
.join("\n");
|
|
791
|
+
for (const descriptor of getMessageQueueMetricsForSystem(system)) {
|
|
792
|
+
expect(text).toContain(`\`${descriptor.metricName}\``);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
test("the type column follows the metric's kind and aggregation", () => {
|
|
798
|
+
const base: MessageQueueMetricDescriptor =
|
|
799
|
+
getMessageQueueMetricsForSystem("kafka")[0]!;
|
|
800
|
+
expect(getMessageQueueMetricTypeLabel({ ...base, kind: "counter" })).toBe(
|
|
801
|
+
"Counter, charted per second",
|
|
802
|
+
);
|
|
803
|
+
expect(
|
|
804
|
+
getMessageQueueMetricTypeLabel({
|
|
805
|
+
...base,
|
|
806
|
+
kind: "gauge",
|
|
807
|
+
aggregation: AggregationType.Sum,
|
|
808
|
+
}),
|
|
809
|
+
).toBe("Count per period");
|
|
810
|
+
for (const aggregation of [
|
|
811
|
+
AggregationType.Max,
|
|
812
|
+
AggregationType.Avg,
|
|
813
|
+
AggregationType.Min,
|
|
814
|
+
]) {
|
|
815
|
+
expect(
|
|
816
|
+
getMessageQueueMetricTypeLabel({
|
|
817
|
+
...base,
|
|
818
|
+
kind: "gauge",
|
|
819
|
+
aggregation: aggregation as AggregationType.Max,
|
|
820
|
+
}),
|
|
821
|
+
).toBe("Gauge");
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
test("JMS says an ActiveMQ broker's metrics chart once they arrive", () => {
|
|
826
|
+
const topic: SetupGuideTopic = findTopic(
|
|
827
|
+
queueContent(sampleQueue("jms")).advanced,
|
|
828
|
+
"What this queue's page charts",
|
|
829
|
+
);
|
|
830
|
+
expect(topic.markdown).toContain(
|
|
831
|
+
"Once an Apache ActiveMQ broker's metrics arrive, the queue shows as an Apache ActiveMQ queue",
|
|
832
|
+
);
|
|
833
|
+
expect(topic.markdown).toContain(getMessageQueueDocsUrl("activemq"));
|
|
834
|
+
});
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
describe("the product guide", () => {
|
|
838
|
+
test.each(SYSTEMS)(
|
|
839
|
+
"%s opens with how its queues appear",
|
|
840
|
+
(system: string) => {
|
|
841
|
+
const descriptor: MessagingSystemDescriptor = descriptorOf(system);
|
|
842
|
+
const content: SetupGuideContent = productContent(system);
|
|
843
|
+
const intro: string = content.intro || "";
|
|
844
|
+
const markdown: string = getSetupGuideMarkdown(content);
|
|
845
|
+
|
|
846
|
+
// The card's title names the guide: the intro starts with its first sentence.
|
|
847
|
+
expect(
|
|
848
|
+
intro.startsWith(
|
|
849
|
+
`${descriptor.displayName} queues appear in OneUptime on their own`,
|
|
850
|
+
),
|
|
851
|
+
).toBe(true);
|
|
852
|
+
expect(intro).toContain("**Queues → Create Queue**");
|
|
853
|
+
expect(intro).toContain(
|
|
854
|
+
`\`messaging.system\` = ${markdownInlineCode(descriptor.system)}`,
|
|
855
|
+
);
|
|
856
|
+
for (const alias of descriptor.aliases) {
|
|
857
|
+
expect(intro).toContain(markdownInlineCode(alias));
|
|
858
|
+
}
|
|
859
|
+
/*
|
|
860
|
+
* Broker metrics are promised only where they name a queue: a system
|
|
861
|
+
* with curated metrics. A collector path alone is not enough — NATS
|
|
862
|
+
* and Azure Event Grid have one, and their metrics attach to no queue.
|
|
863
|
+
*/
|
|
864
|
+
expect(markdown.includes("and the broker's own metrics")).toBe(
|
|
865
|
+
getMessageQueueMetricsForSystem(system).length > 0,
|
|
866
|
+
);
|
|
867
|
+
expect(content.links).toContainEqual({
|
|
868
|
+
title: `${descriptor.displayName} in the Queues documentation`,
|
|
869
|
+
url: `${MESSAGE_QUEUE_DOCS_PATH}#${descriptor.docsAnchor}`,
|
|
870
|
+
});
|
|
871
|
+
/*
|
|
872
|
+
* The catalog's own note or reason, verbatim: the note in the broker
|
|
873
|
+
* step, BullMQ's reason in the step that reports its depth, and the
|
|
874
|
+
* reason of a system with no step for it in the intro.
|
|
875
|
+
*/
|
|
876
|
+
const source: MessagingBrokerMetricsSource = descriptor.brokerMetrics;
|
|
877
|
+
if (source.kind !== "none") {
|
|
878
|
+
expect(
|
|
879
|
+
findStep(content, "Send the broker's metrics").markdown,
|
|
880
|
+
).toContain(source.note);
|
|
881
|
+
} else if (system === "bullmq") {
|
|
882
|
+
expect(
|
|
883
|
+
findStep(content, "Report the queue's depth").markdown,
|
|
884
|
+
).toContain(source.reason);
|
|
885
|
+
} else {
|
|
886
|
+
expect(intro).toContain(source.reason);
|
|
887
|
+
}
|
|
888
|
+
},
|
|
889
|
+
);
|
|
890
|
+
|
|
891
|
+
test("the steps follow the source", () => {
|
|
892
|
+
expect(stepTitles(productContent("kafka"))).toEqual([
|
|
893
|
+
"Instrument the applications that use them",
|
|
894
|
+
"Send the broker's metrics",
|
|
895
|
+
"Check that your queues appear",
|
|
896
|
+
]);
|
|
897
|
+
expect(stepTitles(productContent("bullmq"))).toEqual([
|
|
898
|
+
"Tag BullMQ's telemetry in your collector",
|
|
899
|
+
"Report the queue's depth",
|
|
900
|
+
"Check that your queues appear",
|
|
901
|
+
]);
|
|
902
|
+
// No ready-made metrics path: no broker step, and the reason up front.
|
|
903
|
+
expect(stepTitles(productContent("jms"))).toEqual([
|
|
904
|
+
"Instrument the applications that use them",
|
|
905
|
+
"Check that your queues appear",
|
|
906
|
+
]);
|
|
907
|
+
expect(stepTitles(queueContent(LONG_TAIL_QUEUE))).toEqual([
|
|
908
|
+
"Instrument the applications that use it",
|
|
909
|
+
"Check that this queue fills in",
|
|
910
|
+
]);
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
test("JMS's ActiveMQ scraper is an Advanced topic, not a step", () => {
|
|
914
|
+
for (const content of [
|
|
915
|
+
productContent("jms"),
|
|
916
|
+
queueContent(sampleQueue("jms")),
|
|
917
|
+
]) {
|
|
918
|
+
const topic: SetupGuideTopic = findTopic(
|
|
919
|
+
content.advanced,
|
|
920
|
+
"If the broker is Apache ActiveMQ",
|
|
921
|
+
);
|
|
922
|
+
expect(topic.markdown).toContain(
|
|
923
|
+
"java -jar opentelemetry-jmx-scraper.jar",
|
|
924
|
+
);
|
|
925
|
+
expect(topic.markdown).toContain("ACTIVEMQ_SUNJMX_START");
|
|
926
|
+
expect(topic.markdown).toContain(
|
|
927
|
+
"which from then on shows as an Apache ActiveMQ queue.",
|
|
928
|
+
);
|
|
929
|
+
for (const step of content.steps) {
|
|
930
|
+
expect(step.markdown || "").not.toContain("opentelemetry-jmx-scraper");
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
// Only JMS gets it: ActiveMQ's own guide has the scraper as its broker step.
|
|
934
|
+
for (const system of SYSTEMS) {
|
|
935
|
+
expect(
|
|
936
|
+
topicTitles(productContent(system).advanced).includes(
|
|
937
|
+
"If the broker is Apache ActiveMQ",
|
|
938
|
+
),
|
|
939
|
+
).toBe(system === "jms");
|
|
940
|
+
}
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
test("every docs link lands on a heading of the docs page", () => {
|
|
944
|
+
for (const descriptor of MESSAGING_SYSTEMS) {
|
|
945
|
+
expect(QUEUES_DOC).toContain(`\n### ${descriptor.displayName}\n`);
|
|
946
|
+
expect(getMessageQueueDocsUrl(descriptor.system)).toBe(
|
|
947
|
+
`/docs/telemetry/queues#${descriptor.docsAnchor}`,
|
|
948
|
+
);
|
|
949
|
+
}
|
|
950
|
+
expect(QUEUES_DOC).toContain("\n## Supported messaging systems\n");
|
|
951
|
+
expect(getMessageQueueDocsUrl("ibmmq")).toBe(
|
|
952
|
+
`/docs/telemetry/queues#${MESSAGE_QUEUE_SUPPORTED_SYSTEMS_ANCHOR}`,
|
|
953
|
+
);
|
|
954
|
+
expect(getMessageQueueDocsUrl(undefined)).toBe(
|
|
955
|
+
"/docs/telemetry/queues#supported-messaging-systems",
|
|
956
|
+
);
|
|
957
|
+
// The awsfirehose pointer.
|
|
958
|
+
expect(QUEUES_DOC).toContain("\n### Amazon SQS\n");
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
test("every docs link any guide carries lands on a heading of the docs page", () => {
|
|
962
|
+
const anchors: Set<string> = new Set<string>();
|
|
963
|
+
for (const [, content] of ALL_GUIDES) {
|
|
964
|
+
for (const link of getSetupGuideMarkdown(content).matchAll(
|
|
965
|
+
/\]\(\/docs\/telemetry\/queues#([^)\s]*)\)/g,
|
|
966
|
+
)) {
|
|
967
|
+
anchors.add(link[1]!);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// The system sections, the fallback, the Metric Stream, Late metrics and troubleshooting.
|
|
972
|
+
expect(anchors).toContain("amazon-sqs");
|
|
973
|
+
expect(anchors).toContain("alerting");
|
|
974
|
+
expect(anchors).toContain("broker-health-stays-empty");
|
|
975
|
+
expect(anchors).toContain("the-auto-create-budget");
|
|
976
|
+
expect(anchors).toContain(MESSAGE_QUEUE_SUPPORTED_SYSTEMS_ANCHOR);
|
|
977
|
+
for (const anchor of anchors) {
|
|
978
|
+
expect({ anchor, resolves: DOCS_SLUGS.has(anchor) }).toEqual({
|
|
979
|
+
anchor,
|
|
980
|
+
resolves: true,
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
test("the picker's systems are the catalog's, alphabetical", () => {
|
|
986
|
+
const names: Array<string> = getMessageQueueGuideSystems().map(
|
|
987
|
+
(descriptor: MessagingSystemDescriptor): string => {
|
|
988
|
+
return descriptor.displayName;
|
|
989
|
+
},
|
|
990
|
+
);
|
|
991
|
+
expect([...names].sort()).toEqual(
|
|
992
|
+
MESSAGING_SYSTEMS.map((descriptor: MessagingSystemDescriptor): string => {
|
|
993
|
+
return descriptor.displayName;
|
|
994
|
+
}).sort(),
|
|
995
|
+
);
|
|
996
|
+
expect(names).toEqual(
|
|
997
|
+
[...names].sort((a: string, b: string): number => {
|
|
998
|
+
return a.localeCompare(b);
|
|
999
|
+
}),
|
|
1000
|
+
);
|
|
1001
|
+
});
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
describe("the picker", () => {
|
|
1005
|
+
test("offers one option per catalog system, in the guide's order, keyed by messaging.system", () => {
|
|
1006
|
+
expect(MESSAGE_QUEUE_SETUP_GUIDE_OPTIONS).toEqual(
|
|
1007
|
+
getMessageQueueGuideSystems().map(
|
|
1008
|
+
(descriptor: MessagingSystemDescriptor): SetupGuideOption => {
|
|
1009
|
+
return {
|
|
1010
|
+
key: descriptor.system,
|
|
1011
|
+
label: descriptor.displayName,
|
|
1012
|
+
description: `messaging.system: ${descriptor.system}`,
|
|
1013
|
+
};
|
|
1014
|
+
},
|
|
1015
|
+
),
|
|
1016
|
+
);
|
|
1017
|
+
expect(MESSAGE_QUEUE_SETUP_GUIDE_OPTIONS).toHaveLength(
|
|
1018
|
+
MESSAGING_SYSTEMS.length,
|
|
1019
|
+
);
|
|
1020
|
+
});
|
|
1021
|
+
|
|
1022
|
+
test("has no groups or badges, and one-line tooltips", () => {
|
|
1023
|
+
for (const option of MESSAGE_QUEUE_SETUP_GUIDE_OPTIONS) {
|
|
1024
|
+
expect(option.group).toBeUndefined();
|
|
1025
|
+
expect(option.badge).toBeUndefined();
|
|
1026
|
+
expect(option.description).not.toContain("\n");
|
|
1027
|
+
}
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
test("opens on Kafka, which it offers", () => {
|
|
1031
|
+
expect(DEFAULT_MESSAGE_QUEUE_GUIDE_SYSTEM).toBe("kafka");
|
|
1032
|
+
expect(
|
|
1033
|
+
MESSAGE_QUEUE_SETUP_GUIDE_OPTIONS.map((option: SetupGuideOption) => {
|
|
1034
|
+
return option.key;
|
|
1035
|
+
}),
|
|
1036
|
+
).toContain(DEFAULT_MESSAGE_QUEUE_GUIDE_SYSTEM);
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
test("a system resolves through its aliases, and an unknown one to Kafka", () => {
|
|
1040
|
+
expect(resolveMessageQueueGuideSystem("azure_servicebus")).toBe(
|
|
1041
|
+
"servicebus",
|
|
1042
|
+
);
|
|
1043
|
+
expect(resolveMessageQueueGuideSystem("AmazonSQS")).toBe("aws_sqs");
|
|
1044
|
+
expect(resolveMessageQueueGuideSystem("ibmmq")).toBe("kafka");
|
|
1045
|
+
expect(resolveMessageQueueGuideSystem(undefined)).toBe("kafka");
|
|
1046
|
+
for (const option of MESSAGE_QUEUE_SETUP_GUIDE_OPTIONS) {
|
|
1047
|
+
expect(resolveMessageQueueGuideSystem(option.key)).toBe(option.key);
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
});
|
|
1051
|
+
|
|
1052
|
+
describe("the instrumentation notes", () => {
|
|
1053
|
+
test("Kafka names a way for every major language", () => {
|
|
1054
|
+
const languages: Array<string> = getMessagingInstrumentationNotes(
|
|
1055
|
+
"kafka",
|
|
1056
|
+
).map((note: { language: string }): string => {
|
|
1057
|
+
return note.language;
|
|
1058
|
+
});
|
|
1059
|
+
expect(languages).toEqual(["Java", ".NET", "Node.js", "Python", "Go"]);
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
test("Service Bus and Event Hubs share the Azure SDK switches", () => {
|
|
1063
|
+
for (const system of ["servicebus", "eventhubs"]) {
|
|
1064
|
+
const markdown: string = productContent(system).steps[0]!.markdown || "";
|
|
1065
|
+
expect(markdown).toContain(
|
|
1066
|
+
"AZURE_EXPERIMENTAL_ENABLE_ACTIVITY_SOURCE=true",
|
|
1067
|
+
);
|
|
1068
|
+
expect(markdown).toContain('.AddSource("Azure.*")');
|
|
1069
|
+
expect(markdown).toContain("createAzureSdkInstrumentation()");
|
|
1070
|
+
expect(markdown).toContain("azure-core-tracing-opentelemetry");
|
|
1071
|
+
}
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
test("JMS and ActiveMQ explain the family", () => {
|
|
1075
|
+
for (const system of ["jms", "activemq"]) {
|
|
1076
|
+
expect(productGuide(system)).toContain(
|
|
1077
|
+
"JMS spans say `jms` whichever broker is behind them",
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
test("the AWS notes ask .NET for the latest attribute set", () => {
|
|
1083
|
+
for (const system of ["aws_sqs", "aws.sns"]) {
|
|
1084
|
+
expect(productGuide(system)).toContain(
|
|
1085
|
+
"SemanticConventionVersion.Latest",
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
|
|
1090
|
+
test("every guide ends its client list with the generic advice for its own system", () => {
|
|
1091
|
+
for (const system of SYSTEMS) {
|
|
1092
|
+
if (system === "bullmq") {
|
|
1093
|
+
continue;
|
|
1094
|
+
}
|
|
1095
|
+
expect(productContent(system).steps[0]!.markdown).toContain(
|
|
1096
|
+
`set \`messaging.system\` = ${markdownInlineCode(system)}, \`messaging.destination.name\` and the span kind`,
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1101
|
+
test("BullMQ gets the transform that adds the keys OneUptime reads", () => {
|
|
1102
|
+
const content: SetupGuideContent = productContent("bullmq");
|
|
1103
|
+
const transform: YamlMap = yamlBlocks(
|
|
1104
|
+
findStep(content, "Tag BullMQ's telemetry in your collector").markdown ||
|
|
1105
|
+
"",
|
|
1106
|
+
)[0]!;
|
|
1107
|
+
expect(Object.keys(transform["service"]["pipelines"]).sort()).toEqual([
|
|
1108
|
+
"metrics",
|
|
1109
|
+
"traces",
|
|
1110
|
+
]);
|
|
1111
|
+
expect(
|
|
1112
|
+
transform["processors"]["transform/bullmq"]["trace_statements"].join(
|
|
1113
|
+
"\n",
|
|
1114
|
+
),
|
|
1115
|
+
).toContain('set(span.attributes["messaging.system"], "bullmq")');
|
|
1116
|
+
expect(findStep(content, "Report the queue's depth").markdown).toContain(
|
|
1117
|
+
'createObservableGauge("queue.size"',
|
|
1118
|
+
);
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
test("every SDK environment block keeps the note that the token is a secret", () => {
|
|
1122
|
+
for (const [, content] of ALL_GUIDES) {
|
|
1123
|
+
const markdown: string = getSetupGuideMarkdown(content);
|
|
1124
|
+
if (codeBlocks(markdown).some(isSdkEnvironmentBlock)) {
|
|
1125
|
+
expect(markdown).toContain(
|
|
1126
|
+
"> **This token is a secret.** Use a **Server** ingestion key",
|
|
1127
|
+
);
|
|
1128
|
+
expect(markdown).toContain(
|
|
1129
|
+
"Never put a Server key in browser JavaScript",
|
|
1130
|
+
);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1134
|
+
});
|
|
1135
|
+
|
|
1136
|
+
describe("a queue's own guide", () => {
|
|
1137
|
+
test("says how its telemetry finds it", () => {
|
|
1138
|
+
const content: SetupGuideContent = queueContent({
|
|
1139
|
+
system: "kafka",
|
|
1140
|
+
destination: "orders.created",
|
|
1141
|
+
brokerAddress: "",
|
|
1142
|
+
});
|
|
1143
|
+
expect(
|
|
1144
|
+
(content.intro || "").startsWith(
|
|
1145
|
+
"This is the Apache Kafka queue `orders.created`.",
|
|
1146
|
+
),
|
|
1147
|
+
).toBe(true);
|
|
1148
|
+
expect(content.intro).toContain(
|
|
1149
|
+
"Everything below is prefilled for this queue.",
|
|
1150
|
+
);
|
|
1151
|
+
const identity: SetupGuideTopic = findTopic(
|
|
1152
|
+
content.advanced,
|
|
1153
|
+
"How telemetry finds this queue",
|
|
1154
|
+
);
|
|
1155
|
+
expect(identity.markdown).toContain(
|
|
1156
|
+
"- **Spans**: `messaging.system` = `kafka` and the destination `orders.created`",
|
|
1157
|
+
);
|
|
1158
|
+
expect(identity.markdown).toContain(
|
|
1159
|
+
"- **Broker metrics**: `topic` = `orders.created`.",
|
|
1160
|
+
);
|
|
1161
|
+
expect(identity.markdown).toContain(
|
|
1162
|
+
"Names are compared without regard to case.",
|
|
1163
|
+
);
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
test("the product guide has no identity topic: it names no queue", () => {
|
|
1167
|
+
for (const system of SYSTEMS) {
|
|
1168
|
+
expect(topicTitles(productContent(system).advanced)).not.toContain(
|
|
1169
|
+
"How telemetry finds this queue",
|
|
1170
|
+
);
|
|
1171
|
+
expect(topicTitles(queueContent(sampleQueue(system)).advanced)[0]).toBe(
|
|
1172
|
+
"How telemetry finds this queue",
|
|
1173
|
+
);
|
|
1174
|
+
}
|
|
1175
|
+
});
|
|
1176
|
+
|
|
1177
|
+
test("an Azure queue names its namespace on both sides", () => {
|
|
1178
|
+
const markdown: string = queueGuide({
|
|
1179
|
+
system: "servicebus",
|
|
1180
|
+
destination: "orders",
|
|
1181
|
+
brokerScope: "shop-prod",
|
|
1182
|
+
});
|
|
1183
|
+
expect(markdown).toContain(
|
|
1184
|
+
"This is the Azure Service Bus queue `orders` in the `shop-prod` namespace.",
|
|
1185
|
+
);
|
|
1186
|
+
expect(markdown).toContain(
|
|
1187
|
+
"from an SDK connected to `shop-prod.servicebus.windows.net`",
|
|
1188
|
+
);
|
|
1189
|
+
expect(markdown).toContain(
|
|
1190
|
+
"- **Broker metrics**: `metadata_entityname` = `orders`, on the namespace whose `name` is `shop-prod`.",
|
|
1191
|
+
);
|
|
1192
|
+
// Every accepted spelling keys the same queue.
|
|
1193
|
+
expect(markdown).toContain(
|
|
1194
|
+
"`messaging.system` = `servicebus` (or `azure_servicebus`, `azure.servicebus` or `microsoft.servicebus`)",
|
|
1195
|
+
);
|
|
1196
|
+
});
|
|
1197
|
+
|
|
1198
|
+
test("an Azure queue without a namespace says Azure Monitor's metrics go elsewhere", () => {
|
|
1199
|
+
const markdown: string = queueGuide({
|
|
1200
|
+
system: "eventhubs",
|
|
1201
|
+
destination: "telemetry",
|
|
1202
|
+
brokerScope: "",
|
|
1203
|
+
});
|
|
1204
|
+
expect(markdown).toContain(
|
|
1205
|
+
"from an SDK connected to a host that names no namespace",
|
|
1206
|
+
);
|
|
1207
|
+
expect(markdown).toContain(
|
|
1208
|
+
"Azure Monitor names the namespace of every metric it reports",
|
|
1209
|
+
);
|
|
1210
|
+
expect(markdown).not.toContain("on the namespace whose");
|
|
1211
|
+
});
|
|
1212
|
+
|
|
1213
|
+
test("an ActiveMQ queue is found by its JMS family's spans too", () => {
|
|
1214
|
+
const markdown: string = queueGuide({
|
|
1215
|
+
system: "activemq",
|
|
1216
|
+
destination: "orders",
|
|
1217
|
+
});
|
|
1218
|
+
expect(markdown).toContain(
|
|
1219
|
+
"`messaging.system` = `jms` (or `activemq`, `artemis` or `activemq_artemis`)",
|
|
1220
|
+
);
|
|
1221
|
+
expect(markdown).toContain(
|
|
1222
|
+
"JMS clients' spans say `jms` whichever broker is behind them, so they and the Apache ActiveMQ broker's metrics land on this one queue.",
|
|
1223
|
+
);
|
|
1224
|
+
expect(markdown).toContain(
|
|
1225
|
+
"- **Broker metrics**: `messaging.destination.name` or `destination` = `orders`.",
|
|
1226
|
+
);
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
test("the broker-metric keys come from the catalog, one per spelling", () => {
|
|
1230
|
+
expect(getMessageQueueDestinationAttributeKeys("servicebus")).toEqual([
|
|
1231
|
+
"metadata_entityname",
|
|
1232
|
+
]);
|
|
1233
|
+
expect(getMessageQueueDestinationAttributeKeys("aws_sqs")).toEqual([
|
|
1234
|
+
"Dimensions.QueueName",
|
|
1235
|
+
"QueueName",
|
|
1236
|
+
]);
|
|
1237
|
+
expect(getMessageQueueDestinationAttributeKeys("gcp_pubsub")).toEqual([
|
|
1238
|
+
"resource.subscription_id",
|
|
1239
|
+
"resource.topic_id",
|
|
1240
|
+
]);
|
|
1241
|
+
expect(getMessageQueueDestinationAttributeKeys("nats")).toEqual([]);
|
|
1242
|
+
});
|
|
1243
|
+
|
|
1244
|
+
test("a system without broker metrics promises only spans and application metrics", () => {
|
|
1245
|
+
const markdown: string = queueGuide({
|
|
1246
|
+
system: "bullmq",
|
|
1247
|
+
destination: "orders",
|
|
1248
|
+
});
|
|
1249
|
+
expect(markdown).toContain(
|
|
1250
|
+
"and from metrics that carry its `messaging.system` and `messaging.destination.name`",
|
|
1251
|
+
);
|
|
1252
|
+
expect(markdown).not.toContain("**Broker metrics**:");
|
|
1253
|
+
expect(markdown).not.toContain("the broker's own metrics");
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
test("a long-tail system gets the generic guide", () => {
|
|
1257
|
+
const content: SetupGuideContent = queueContent(LONG_TAIL_QUEUE);
|
|
1258
|
+
const markdown: string = getSetupGuideMarkdown(content);
|
|
1259
|
+
expect(content.intro).toContain("This is the ibmmq queue `DEV.QUEUE.1`.");
|
|
1260
|
+
expect(markdown).toContain("`messaging.system` = `ibmmq`");
|
|
1261
|
+
// The catalog's reason is in the intro: there is no broker step to hold it.
|
|
1262
|
+
expect(content.intro).toContain(
|
|
1263
|
+
"OneUptime does not know this messaging system",
|
|
1264
|
+
);
|
|
1265
|
+
expect(yamlBlocks(markdown)).toEqual([]);
|
|
1266
|
+
expect(markdown).toContain(
|
|
1267
|
+
"(/docs/telemetry/queues#supported-messaging-systems)",
|
|
1268
|
+
);
|
|
1269
|
+
expect(content.links).toEqual([
|
|
1270
|
+
{
|
|
1271
|
+
title: "Supported messaging systems",
|
|
1272
|
+
url: "/docs/telemetry/queues#supported-messaging-systems",
|
|
1273
|
+
},
|
|
1274
|
+
{ title: "Queues documentation", url: "/docs/telemetry/queues" },
|
|
1275
|
+
]);
|
|
1276
|
+
});
|
|
1277
|
+
|
|
1278
|
+
test("a queue with no system still renders", () => {
|
|
1279
|
+
const content: SetupGuideContent = queueContent({
|
|
1280
|
+
system: null,
|
|
1281
|
+
destination: "x",
|
|
1282
|
+
});
|
|
1283
|
+
const markdown: string = getSetupGuideMarkdown(content);
|
|
1284
|
+
expect(content.intro).toContain("This is the — queue `x`.");
|
|
1285
|
+
expect(markdown).toContain("`messaging.system` = `<system>`");
|
|
1286
|
+
expect(markdown).toContain(
|
|
1287
|
+
"- **Spans**: `messaging.system` = `<system>` and the destination `x`",
|
|
1288
|
+
);
|
|
1289
|
+
// No empty code span (two backticks that are not part of a fence).
|
|
1290
|
+
expect(markdown).not.toMatch(/(^|[^`])``([^`]|$)/m);
|
|
1291
|
+
});
|
|
1292
|
+
|
|
1293
|
+
/*
|
|
1294
|
+
* The guide names a queue's system as every other Queues page does
|
|
1295
|
+
* (getMessageQueueSystemLabel): the list's System cell, the Overview and
|
|
1296
|
+
* the Documentation tab's own title all read "—" for a queue without one,
|
|
1297
|
+
* so its guide does too rather than a wording of its own.
|
|
1298
|
+
*/
|
|
1299
|
+
test.each([null, undefined, "", " "])(
|
|
1300
|
+
"a queue whose system is %p is named as the list names it",
|
|
1301
|
+
(system: string | null | undefined) => {
|
|
1302
|
+
const label: string = getMessageQueueSystemLabel(system);
|
|
1303
|
+
expect(label).toBe("—");
|
|
1304
|
+
|
|
1305
|
+
const markdown: string = queueGuide({ system: system, destination: "x" });
|
|
1306
|
+
expect(markdown).toContain(`This is the ${label} queue \`x\`.`);
|
|
1307
|
+
expect(markdown).toContain(
|
|
1308
|
+
`OneUptime charts no broker metrics for ${label} under **Broker health**.`,
|
|
1309
|
+
);
|
|
1310
|
+
expect(markdown).not.toContain("the this messaging system");
|
|
1311
|
+
expect(markdown).not.toContain("for this messaging system under");
|
|
1312
|
+
},
|
|
1313
|
+
);
|
|
1314
|
+
|
|
1315
|
+
test.each([
|
|
1316
|
+
["kafka", "Apache Kafka"],
|
|
1317
|
+
["azure_servicebus", "Azure Service Bus"],
|
|
1318
|
+
["IBMMQ", "ibmmq"],
|
|
1319
|
+
])(
|
|
1320
|
+
"a queue of %p is named %p, as the list names it",
|
|
1321
|
+
(system: string, label: string) => {
|
|
1322
|
+
expect(
|
|
1323
|
+
queueGuide({ system: system, destination: "x", brokerScope: "shop" }),
|
|
1324
|
+
).toContain(`This is the ${label} queue \`x\``);
|
|
1325
|
+
},
|
|
1326
|
+
);
|
|
1327
|
+
});
|
|
1328
|
+
|
|
1329
|
+
/*
|
|
1330
|
+
* The guide promises broker metrics only where they can reach the queue —
|
|
1331
|
+
* the rule the Overview's Broker health section follows (a system with
|
|
1332
|
+
* curated metrics). A collector path is not enough: NATS's exporter reports
|
|
1333
|
+
* JetStream streams and consumers, not subjects, and Azure Monitor reports
|
|
1334
|
+
* Event Grid per resource and event subscription, so neither ever creates
|
|
1335
|
+
* or fills a queue. Nor does Azure Monitor reach a Service Bus / Event Hubs
|
|
1336
|
+
* queue without a namespace: it names the namespace of every metric.
|
|
1337
|
+
*/
|
|
1338
|
+
describe("broker metrics are promised only where they reach the queue", () => {
|
|
1339
|
+
// Computed, so a system that gains curated metrics leaves the list.
|
|
1340
|
+
const UNCHARTED_WITH_A_COLLECTOR: Array<string> = SYSTEMS.filter(
|
|
1341
|
+
(system: string): boolean => {
|
|
1342
|
+
return (
|
|
1343
|
+
descriptorOf(system).brokerMetrics.kind !== "none" &&
|
|
1344
|
+
getMessageQueueMetricsForSystem(system).length === 0
|
|
1345
|
+
);
|
|
1346
|
+
},
|
|
1347
|
+
);
|
|
1348
|
+
|
|
1349
|
+
test("the catalog has systems a collector reads but no queue charts", () => {
|
|
1350
|
+
// NATS and Azure Event Grid today; keeps the next test from passing empty.
|
|
1351
|
+
expect(UNCHARTED_WITH_A_COLLECTOR.length).toBeGreaterThan(0);
|
|
1352
|
+
});
|
|
1353
|
+
|
|
1354
|
+
test.each(UNCHARTED_WITH_A_COLLECTOR)(
|
|
1355
|
+
"%s's guides promise spans and application metrics, and send the collector's metrics to the Metrics explorer",
|
|
1356
|
+
(system: string) => {
|
|
1357
|
+
const displayName: string = descriptorOf(system).displayName;
|
|
1358
|
+
const productGuideContent: SetupGuideContent = productContent(system);
|
|
1359
|
+
const queueGuideContent: SetupGuideContent = queueContent(
|
|
1360
|
+
sampleQueue(system),
|
|
1361
|
+
);
|
|
1362
|
+
|
|
1363
|
+
expect(productGuideContent.intro).toContain(
|
|
1364
|
+
`${displayName} queues appear in OneUptime on their own, from the messaging spans of the applications that publish to and consume from them.`,
|
|
1365
|
+
);
|
|
1366
|
+
expect(queueGuideContent.intro).toContain(
|
|
1367
|
+
"and from metrics that carry its `messaging.system` and `messaging.destination.name`.",
|
|
1368
|
+
);
|
|
1369
|
+
for (const content of [productGuideContent, queueGuideContent]) {
|
|
1370
|
+
const markdown: string = getSetupGuideMarkdown(content);
|
|
1371
|
+
expect(markdown).not.toContain("the broker's own metrics");
|
|
1372
|
+
expect(markdown).not.toContain("Overview charts these");
|
|
1373
|
+
expect(markdown).not.toContain("creates its queue too");
|
|
1374
|
+
// The collector config stays: its metrics are worth exploring.
|
|
1375
|
+
const broker: SetupGuideStep = findStep(
|
|
1376
|
+
content,
|
|
1377
|
+
"Send the broker's metrics",
|
|
1378
|
+
);
|
|
1379
|
+
expect(broker.description).toBe(
|
|
1380
|
+
"No queue page charts them: they arrive in the Metrics explorer.",
|
|
1381
|
+
);
|
|
1382
|
+
expect(yamlBlocks(markdown)).toHaveLength(1);
|
|
1383
|
+
expect(markdown).toContain(
|
|
1384
|
+
`OneUptime charts no broker metrics for ${displayName} under **Broker health**: what the collector above sends arrives only in the **Metrics** explorer.`,
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
},
|
|
1388
|
+
);
|
|
1389
|
+
|
|
1390
|
+
test.each(["servicebus", "eventhubs"])(
|
|
1391
|
+
"a %s queue without a namespace is promised no broker metrics, and the namespaced one is",
|
|
1392
|
+
(system: string) => {
|
|
1393
|
+
const content: SetupGuideContent = queueContent({
|
|
1394
|
+
system,
|
|
1395
|
+
destination: "orders",
|
|
1396
|
+
brokerScope: "",
|
|
1397
|
+
brokerAddress: "localhost:5672",
|
|
1398
|
+
});
|
|
1399
|
+
const markdown: string = getSetupGuideMarkdown(content);
|
|
1400
|
+
expect(markdown).toContain(
|
|
1401
|
+
"and from metrics that carry its `messaging.system` and `messaging.destination.name`.",
|
|
1402
|
+
);
|
|
1403
|
+
expect(markdown).not.toContain("the broker's own metrics");
|
|
1404
|
+
expect(markdown).not.toContain("The queue's Overview charts these");
|
|
1405
|
+
expect(markdown).toContain(
|
|
1406
|
+
"This queue has no namespace, so Azure Monitor's metrics never reach it: they chart under **Broker health** on the same destination's queue in their namespace, where each gauge and each count per period has **Create monitor**.",
|
|
1407
|
+
);
|
|
1408
|
+
expect(findStep(content, "Send the broker's metrics").description).toBe(
|
|
1409
|
+
"They chart on the same destination's queue in its namespace, not on this one.",
|
|
1410
|
+
);
|
|
1411
|
+
expect(
|
|
1412
|
+
findStep(content, "Check that this queue fills in").markdown,
|
|
1413
|
+
).toContain(
|
|
1414
|
+
"Azure Monitor's metrics chart under **Broker health** on the same destination's queue in its namespace, not on this one.",
|
|
1415
|
+
);
|
|
1416
|
+
// The table still says what that namespaced queue charts.
|
|
1417
|
+
for (const row of getMessageQueueChartedMetricRows(system)) {
|
|
1418
|
+
expect(markdown).toContain(
|
|
1419
|
+
`| ${row.metric} | ${row.title} | ${row.type} |`,
|
|
1420
|
+
);
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
const namespaced: SetupGuideContent = queueContent({
|
|
1424
|
+
system,
|
|
1425
|
+
destination: "orders",
|
|
1426
|
+
brokerScope: "shop-prod",
|
|
1427
|
+
});
|
|
1428
|
+
const namespacedMarkdown: string = getSetupGuideMarkdown(namespaced);
|
|
1429
|
+
expect(namespacedMarkdown).toContain(
|
|
1430
|
+
"and from the broker's own metrics.",
|
|
1431
|
+
);
|
|
1432
|
+
expect(namespacedMarkdown).toContain(
|
|
1433
|
+
"The queue's Overview charts these under **Broker health**",
|
|
1434
|
+
);
|
|
1435
|
+
expect(namespacedMarkdown).not.toContain("This queue has no namespace");
|
|
1436
|
+
expect(
|
|
1437
|
+
findStep(namespaced, "Check that this queue fills in").markdown,
|
|
1438
|
+
).toContain(
|
|
1439
|
+
"**Broker health** on the Overview fills in from the broker's metrics as they arrive.",
|
|
1440
|
+
);
|
|
1441
|
+
},
|
|
1442
|
+
);
|
|
1443
|
+
|
|
1444
|
+
test("only the namespace systems wait up to a day for a new namespace", () => {
|
|
1445
|
+
const waiting: Array<string> = [];
|
|
1446
|
+
for (const system of SYSTEMS) {
|
|
1447
|
+
for (const markdown of [
|
|
1448
|
+
productGuide(system),
|
|
1449
|
+
queueGuide(sampleQueue(system)),
|
|
1450
|
+
]) {
|
|
1451
|
+
if (
|
|
1452
|
+
markdown.includes("The receiver lists your namespaces once a day")
|
|
1453
|
+
) {
|
|
1454
|
+
waiting.push(system);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
// Both guides of each namespace system, and nothing else.
|
|
1459
|
+
const expected: Array<string> = SYSTEMS.filter(
|
|
1460
|
+
(system: string): boolean => {
|
|
1461
|
+
return descriptorOf(system).brokerScope === "azure-namespace";
|
|
1462
|
+
},
|
|
1463
|
+
).flatMap((system: string): Array<string> => {
|
|
1464
|
+
return [system, system];
|
|
1465
|
+
});
|
|
1466
|
+
expect(expected.length).toBeGreaterThan(0);
|
|
1467
|
+
expect(waiting).toEqual(expected);
|
|
1468
|
+
// Azure Event Grid shares the receiver but has no namespaces to list.
|
|
1469
|
+
expect(waiting).not.toContain("eventgrid");
|
|
1470
|
+
});
|
|
1471
|
+
});
|
|
1472
|
+
|
|
1473
|
+
describe("prefilled from what the queue's telemetry reported", () => {
|
|
1474
|
+
function kafkaBrokers(address: string | null): Array<string> {
|
|
1475
|
+
return yamlBlocks(
|
|
1476
|
+
queueGuide({
|
|
1477
|
+
system: "kafka",
|
|
1478
|
+
destination: "orders",
|
|
1479
|
+
brokerAddress: address,
|
|
1480
|
+
}),
|
|
1481
|
+
)[0]!["receivers"]["kafka_metrics"]["brokers"];
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
test("Kafka's bootstrap broker, with the default port when none was reported", () => {
|
|
1485
|
+
expect(kafkaBrokers("kafka-1.internal:9093")).toEqual([
|
|
1486
|
+
"kafka-1.internal:9093",
|
|
1487
|
+
]);
|
|
1488
|
+
expect(kafkaBrokers("10.0.0.7")).toEqual(["10.0.0.7:9092"]);
|
|
1489
|
+
expect(kafkaBrokers(null)).toEqual(["kafka-1:9092", "kafka-2:9092"]);
|
|
1490
|
+
// Nothing that could break the config is pasted in.
|
|
1491
|
+
expect(kafkaBrokers("[::1]:9092")).toEqual([
|
|
1492
|
+
"kafka-1:9092",
|
|
1493
|
+
"kafka-2:9092",
|
|
1494
|
+
]);
|
|
1495
|
+
expect(kafkaBrokers("tcp://kafka:9092")).toEqual([
|
|
1496
|
+
"kafka-1:9092",
|
|
1497
|
+
"kafka-2:9092",
|
|
1498
|
+
]);
|
|
1499
|
+
expect(kafkaBrokers('kafka"]: 1')).toEqual([
|
|
1500
|
+
"kafka-1:9092",
|
|
1501
|
+
"kafka-2:9092",
|
|
1502
|
+
]);
|
|
1503
|
+
});
|
|
1504
|
+
|
|
1505
|
+
test("the prefill is said out loud, and only when it happened", () => {
|
|
1506
|
+
expect(
|
|
1507
|
+
queueGuide({
|
|
1508
|
+
system: "kafka",
|
|
1509
|
+
destination: "orders",
|
|
1510
|
+
brokerAddress: "kafka-1.internal:9093",
|
|
1511
|
+
}),
|
|
1512
|
+
).toContain(
|
|
1513
|
+
"The bootstrap broker is prefilled from the broker address this queue's telemetry reports (`kafka-1.internal:9093`)",
|
|
1514
|
+
);
|
|
1515
|
+
expect(
|
|
1516
|
+
queueGuide({ system: "kafka", destination: "orders" }),
|
|
1517
|
+
).not.toContain("is prefilled from");
|
|
1518
|
+
});
|
|
1519
|
+
|
|
1520
|
+
test("RabbitMQ's management endpoint on the broker's host", () => {
|
|
1521
|
+
const config: YamlMap = yamlBlocks(
|
|
1522
|
+
queueGuide({
|
|
1523
|
+
system: "rabbitmq",
|
|
1524
|
+
destination: "orders",
|
|
1525
|
+
brokerAddress: "rabbit.internal:5672",
|
|
1526
|
+
}),
|
|
1527
|
+
)[0]!;
|
|
1528
|
+
expect(config["receivers"]["rabbitmq"]["endpoint"]).toBe(
|
|
1529
|
+
"http://rabbit.internal:15672",
|
|
1530
|
+
);
|
|
1531
|
+
});
|
|
1532
|
+
|
|
1533
|
+
test("ActiveMQ's JMX host", () => {
|
|
1534
|
+
const markdown: string = queueGuide({
|
|
1535
|
+
system: "activemq",
|
|
1536
|
+
destination: "orders",
|
|
1537
|
+
brokerAddress: "amq.internal:61616",
|
|
1538
|
+
});
|
|
1539
|
+
expect(markdown).toContain(
|
|
1540
|
+
"export OTEL_JMX_SERVICE_URL=service:jmx:rmi:///jndi/rmi://amq.internal:1099/jmxrmi",
|
|
1541
|
+
);
|
|
1542
|
+
expect(markdown).toContain("-Djava.rmi.server.hostname=amq.internal");
|
|
1543
|
+
expect(markdown).toContain(
|
|
1544
|
+
"The JMX host is prefilled from the broker address this queue's telemetry reports (`amq.internal:61616`).",
|
|
1545
|
+
);
|
|
1546
|
+
});
|
|
1547
|
+
|
|
1548
|
+
test("a JMS queue's ActiveMQ topic scrapes the broker its telemetry reported", () => {
|
|
1549
|
+
const topic: SetupGuideTopic = findTopic(
|
|
1550
|
+
queueContent({
|
|
1551
|
+
system: "jms",
|
|
1552
|
+
destination: "orders",
|
|
1553
|
+
brokerAddress: "amq.internal:61616",
|
|
1554
|
+
}).advanced,
|
|
1555
|
+
"If the broker is Apache ActiveMQ",
|
|
1556
|
+
);
|
|
1557
|
+
expect(topic.markdown).toContain(
|
|
1558
|
+
"export OTEL_JMX_SERVICE_URL=service:jmx:rmi:///jndi/rmi://amq.internal:1099/jmxrmi",
|
|
1559
|
+
);
|
|
1560
|
+
expect(topic.markdown).toContain(
|
|
1561
|
+
"Its metrics join this queue, which from then on shows as an Apache ActiveMQ queue.",
|
|
1562
|
+
);
|
|
1563
|
+
});
|
|
1564
|
+
|
|
1565
|
+
test.each([
|
|
1566
|
+
["aws_sqs", "sqs.eu-west-2.amazonaws.com:443", "eu-west-2"],
|
|
1567
|
+
["aws_sqs", "sqs-fips.us-gov-west-1.amazonaws.com", "us-gov-west-1"],
|
|
1568
|
+
["aws_sqs", "sqs.cn-north-1.amazonaws.com.cn", "cn-north-1"],
|
|
1569
|
+
["aws.sns", "sns.ap-southeast-2.amazonaws.com", "ap-southeast-2"],
|
|
1570
|
+
["aws_sqs", "localhost:4566", "us-east-1"],
|
|
1571
|
+
["aws_sqs", "", "us-east-1"],
|
|
1572
|
+
])(
|
|
1573
|
+
"the %s region from %p is %p",
|
|
1574
|
+
(system: string, address: string, region: string) => {
|
|
1575
|
+
const config: YamlMap = yamlBlocks(
|
|
1576
|
+
queueGuide({ system, destination: "orders", brokerAddress: address }),
|
|
1577
|
+
)[0]!;
|
|
1578
|
+
const receiverId: string = Object.keys(config["receivers"])[0]!;
|
|
1579
|
+
expect(config["receivers"][receiverId]["region"]).toBe(region);
|
|
1580
|
+
},
|
|
1581
|
+
);
|
|
1582
|
+
|
|
1583
|
+
test.each(["pulsar", "rocketmq", "nats"])(
|
|
1584
|
+
"%s keeps its placeholders: its clients reach a proxy or name server, not the metrics endpoint",
|
|
1585
|
+
(system: string) => {
|
|
1586
|
+
const withAddress: string = queueGuide({
|
|
1587
|
+
system,
|
|
1588
|
+
destination: "orders",
|
|
1589
|
+
brokerAddress: "broker-1.internal:6650",
|
|
1590
|
+
});
|
|
1591
|
+
expect(withAddress).not.toContain("broker-1.internal");
|
|
1592
|
+
expect(yamlBlocks(withAddress)).toEqual(
|
|
1593
|
+
yamlBlocks(queueGuide({ system, destination: "orders" })),
|
|
1594
|
+
);
|
|
1595
|
+
},
|
|
1596
|
+
);
|
|
1597
|
+
|
|
1598
|
+
test("BullMQ's gauge observes this queue", () => {
|
|
1599
|
+
expect(
|
|
1600
|
+
queueGuide({ system: "bullmq", destination: 'emails "urgent"' }),
|
|
1601
|
+
).toContain('const queue = new Queue("emails \\"urgent\\"",');
|
|
1602
|
+
});
|
|
1603
|
+
});
|
|
1604
|
+
|
|
1605
|
+
describe("values from data never break a block", () => {
|
|
1606
|
+
test("a key or URL with YAML-significant characters parses back exactly", () => {
|
|
1607
|
+
const vars: GuideVariables = {
|
|
1608
|
+
oneuptimeUrl: "https://one: uptime.example.com #x",
|
|
1609
|
+
apiKey: 'a"b: #c \\ d',
|
|
1610
|
+
};
|
|
1611
|
+
for (const system of SYSTEMS) {
|
|
1612
|
+
for (const config of yamlBlocks(productGuide(system, vars))) {
|
|
1613
|
+
expect(config["exporters"]["otlphttp"]).toEqual({
|
|
1614
|
+
endpoint: `${vars.oneuptimeUrl}/otlp`,
|
|
1615
|
+
headers: { "x-oneuptime-token": vars.apiKey },
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
});
|
|
1620
|
+
|
|
1621
|
+
test("the unconfigured placeholders still parse", () => {
|
|
1622
|
+
const vars: GuideVariables = {
|
|
1623
|
+
oneuptimeUrl: SETUP_GUIDE_URL_PLACEHOLDER,
|
|
1624
|
+
apiKey: SETUP_GUIDE_API_KEY_PLACEHOLDER,
|
|
1625
|
+
};
|
|
1626
|
+
const config: YamlMap = yamlBlocks(productGuide("kafka", vars))[0]!;
|
|
1627
|
+
expect(config["exporters"]["otlphttp"]["endpoint"]).toBe(
|
|
1628
|
+
"<YOUR_ONEUPTIME_URL>/otlp",
|
|
1629
|
+
);
|
|
1630
|
+
expect(
|
|
1631
|
+
config["exporters"]["otlphttp"]["headers"]["x-oneuptime-token"],
|
|
1632
|
+
).toBe("<YOUR_API_KEY>");
|
|
1633
|
+
});
|
|
1634
|
+
|
|
1635
|
+
test("a destination with backticks stays one code span", () => {
|
|
1636
|
+
expect(markdownInlineCode("orders")).toBe("`orders`");
|
|
1637
|
+
expect(markdownInlineCode("a`b")).toBe("``a`b``");
|
|
1638
|
+
expect(markdownInlineCode("a``b`")).toBe("``` a``b` ```");
|
|
1639
|
+
expect(markdownInlineCode("`x")).toBe("`` `x ``");
|
|
1640
|
+
expect(
|
|
1641
|
+
queueContent({ system: "kafka", destination: "we`ird" }).intro,
|
|
1642
|
+
).toContain("This is the Apache Kafka queue ``we`ird``.");
|
|
1643
|
+
});
|
|
1644
|
+
});
|
|
1645
|
+
|
|
1646
|
+
describe("broker address parsing", () => {
|
|
1647
|
+
test.each([
|
|
1648
|
+
["kafka-1.internal:9092", { host: "kafka-1.internal", port: 9092 }],
|
|
1649
|
+
["10.0.0.7", { host: "10.0.0.7", port: null }],
|
|
1650
|
+
[" rabbit ", { host: "rabbit", port: null }],
|
|
1651
|
+
["a:65535", { host: "a", port: 65535 }],
|
|
1652
|
+
["a:0", null],
|
|
1653
|
+
["a:65536", null],
|
|
1654
|
+
["[::1]:9092", null],
|
|
1655
|
+
["tcp://kafka:9092", null],
|
|
1656
|
+
["kafka-1,kafka-2", null],
|
|
1657
|
+
["-kafka", null],
|
|
1658
|
+
["", null],
|
|
1659
|
+
[null, null],
|
|
1660
|
+
[undefined, null],
|
|
1661
|
+
])("%p → %p", (address: string | null | undefined, expected: unknown) => {
|
|
1662
|
+
expect(parseMessageQueueBrokerAddress(address)).toEqual(expected);
|
|
1663
|
+
});
|
|
1664
|
+
|
|
1665
|
+
test.each([
|
|
1666
|
+
["sqs.us-east-1.amazonaws.com", "us-east-1"],
|
|
1667
|
+
["SQS.EU-WEST-1.AMAZONAWS.COM:443", "eu-west-1"],
|
|
1668
|
+
["sns.us-gov-east-1.amazonaws.com", "us-gov-east-1"],
|
|
1669
|
+
["sqs.amazonaws.com", null],
|
|
1670
|
+
["queue.amazonaws.com", null],
|
|
1671
|
+
["sqs.us-east-1.example.com", null],
|
|
1672
|
+
["broker:9092", null],
|
|
1673
|
+
[null, null],
|
|
1674
|
+
])(
|
|
1675
|
+
"the AWS region of %p is %p",
|
|
1676
|
+
(address: string | null, region: unknown) => {
|
|
1677
|
+
expect(getAwsRegionFromBrokerAddress(address)).toBe(region);
|
|
1678
|
+
},
|
|
1679
|
+
);
|
|
1680
|
+
});
|
|
1681
|
+
|
|
1682
|
+
// ---- the SetupGuide layout ---------------------------------------------------
|
|
1683
|
+
|
|
1684
|
+
// The title of the link to a system's docs section.
|
|
1685
|
+
function getMessagingSystemTitle(system: string): string {
|
|
1686
|
+
const descriptor: MessagingSystemDescriptor | undefined =
|
|
1687
|
+
MESSAGING_SYSTEMS.find((candidate: MessagingSystemDescriptor): boolean => {
|
|
1688
|
+
return candidate.system === system;
|
|
1689
|
+
});
|
|
1690
|
+
return descriptor
|
|
1691
|
+
? `${descriptor.displayName} in the Queues documentation`
|
|
1692
|
+
: "Supported messaging systems";
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
describe("every guide follows the SetupGuide layout", () => {
|
|
1696
|
+
test.each(ALL_GUIDES)(
|
|
1697
|
+
"%s: step 1 shows the OTLP endpoint and asks for a Server key",
|
|
1698
|
+
(_name: string, content: SetupGuideContent) => {
|
|
1699
|
+
expect(content.keyStep?.endpointLabel).toBe("OTLP Endpoint");
|
|
1700
|
+
expect(content.keyStep?.endpointValue).toBe(`${VARS.oneuptimeUrl}/otlp`);
|
|
1701
|
+
expect(content.keyStep?.endpointHint).toContain(
|
|
1702
|
+
"add /v1/traces, /v1/metrics and /v1/logs to it",
|
|
1703
|
+
);
|
|
1704
|
+
const description: string = content.keyStep?.description || "";
|
|
1705
|
+
expect(description).toMatch(
|
|
1706
|
+
/^Your applications' OpenTelemetry exporters\b/,
|
|
1707
|
+
);
|
|
1708
|
+
expect(description).toContain("a Server key");
|
|
1709
|
+
expect(description).toContain("never in browser JavaScript");
|
|
1710
|
+
},
|
|
1711
|
+
);
|
|
1712
|
+
|
|
1713
|
+
/*
|
|
1714
|
+
* Step 1 names who sends with the key, and only what this guide sets up:
|
|
1715
|
+
* the collector that reads the broker where the broker step runs one, the
|
|
1716
|
+
* JMX Scraper where the guide runs that, and BullMQ's tagging collector.
|
|
1717
|
+
* A guide with neither (a long-tail system) names only the applications.
|
|
1718
|
+
*/
|
|
1719
|
+
test.each(ALL_GUIDES)(
|
|
1720
|
+
"%s: step 1 names only the senders this guide sets up",
|
|
1721
|
+
(name: string, content: SetupGuideContent) => {
|
|
1722
|
+
const system: string = name.split(" ")[0]!;
|
|
1723
|
+
const description: string = content.keyStep?.description || "";
|
|
1724
|
+
const brokerStep: SetupGuideStep | undefined = content.steps.find(
|
|
1725
|
+
(step: SetupGuideStep): boolean => {
|
|
1726
|
+
return step.title === "Send the broker's metrics";
|
|
1727
|
+
},
|
|
1728
|
+
);
|
|
1729
|
+
const runsCollectorForBroker: boolean = yamlBlocks(
|
|
1730
|
+
brokerStep?.markdown || "",
|
|
1731
|
+
).some((block: YamlMap): boolean => {
|
|
1732
|
+
return Boolean(block["receivers"]);
|
|
1733
|
+
});
|
|
1734
|
+
const runsJmxScraper: boolean = getSetupGuideMarkdown(content).includes(
|
|
1735
|
+
"OTEL_JMX_TARGET_SYSTEM=activemq",
|
|
1736
|
+
);
|
|
1737
|
+
|
|
1738
|
+
expect({
|
|
1739
|
+
system,
|
|
1740
|
+
collector: description.includes("the collector that reads your broker"),
|
|
1741
|
+
}).toEqual({ system, collector: runsCollectorForBroker });
|
|
1742
|
+
expect({
|
|
1743
|
+
system,
|
|
1744
|
+
scraper: description.includes("OpenTelemetry JMX Scraper"),
|
|
1745
|
+
}).toEqual({ system, scraper: runsJmxScraper });
|
|
1746
|
+
expect({
|
|
1747
|
+
system,
|
|
1748
|
+
bullmq: description.includes(
|
|
1749
|
+
"the collector that tags BullMQ's telemetry",
|
|
1750
|
+
),
|
|
1751
|
+
}).toEqual({ system, bullmq: system === "bullmq" });
|
|
1752
|
+
if (system === "jms") {
|
|
1753
|
+
expect(description).toContain(
|
|
1754
|
+
"the OpenTelemetry JMX Scraper if the broker is Apache ActiveMQ",
|
|
1755
|
+
);
|
|
1756
|
+
}
|
|
1757
|
+
if (system === "ibmmq") {
|
|
1758
|
+
expect(description).toMatch(
|
|
1759
|
+
/^Your applications' OpenTelemetry exporters send to OneUptime with this key/,
|
|
1760
|
+
);
|
|
1761
|
+
}
|
|
1762
|
+
},
|
|
1763
|
+
);
|
|
1764
|
+
|
|
1765
|
+
test.each(ALL_GUIDES)(
|
|
1766
|
+
"%s: a few steps, each with a one-line description, ending with the check",
|
|
1767
|
+
(name: string, content: SetupGuideContent) => {
|
|
1768
|
+
const isQueue: boolean = name.endsWith("(queue)");
|
|
1769
|
+
expect(content.steps.length).toBeGreaterThanOrEqual(2);
|
|
1770
|
+
expect(content.steps.length).toBeLessThanOrEqual(3);
|
|
1771
|
+
expect(content.steps[content.steps.length - 1]!.title).toBe(
|
|
1772
|
+
isQueue
|
|
1773
|
+
? "Check that this queue fills in"
|
|
1774
|
+
: "Check that your queues appear",
|
|
1775
|
+
);
|
|
1776
|
+
for (const step of content.steps) {
|
|
1777
|
+
expect((step.description || "").length).toBeGreaterThan(0);
|
|
1778
|
+
expect(step.description).not.toContain("\n");
|
|
1779
|
+
expect((step.markdown || "").length).toBeGreaterThan(0);
|
|
1780
|
+
// The card numbers the steps.
|
|
1781
|
+
expect(step.title).not.toMatch(/^\d/);
|
|
1782
|
+
}
|
|
1783
|
+
},
|
|
1784
|
+
);
|
|
1785
|
+
|
|
1786
|
+
test.each(ALL_GUIDES)(
|
|
1787
|
+
"%s: every Advanced and Troubleshooting topic has a title, a plain one-line summary and a body",
|
|
1788
|
+
(_name: string, content: SetupGuideContent) => {
|
|
1789
|
+
const topics: Array<SetupGuideTopic> = [
|
|
1790
|
+
...(content.advanced || []),
|
|
1791
|
+
...(content.troubleshooting || []),
|
|
1792
|
+
];
|
|
1793
|
+
expect((content.advanced || []).length).toBeGreaterThan(0);
|
|
1794
|
+
expect((content.troubleshooting || []).length).toBe(4);
|
|
1795
|
+
const titles: Array<string> = topicTitles(topics);
|
|
1796
|
+
expect(new Set(titles).size).toBe(titles.length);
|
|
1797
|
+
for (const topic of topics) {
|
|
1798
|
+
expect(topic.title.length).toBeGreaterThan(0);
|
|
1799
|
+
expect(topic.summary).toBeTruthy();
|
|
1800
|
+
// Summaries render as plain text, so no markdown.
|
|
1801
|
+
expect(topic.summary).not.toMatch(/[`*_[\]#<>|]/);
|
|
1802
|
+
expect(topic.summary).not.toContain("\n");
|
|
1803
|
+
expect(topic.markdown.trim().length).toBeGreaterThan(0);
|
|
1804
|
+
}
|
|
1805
|
+
},
|
|
1806
|
+
);
|
|
1807
|
+
|
|
1808
|
+
test.each(ALL_GUIDES)(
|
|
1809
|
+
"%s: no leftover heading, numbering or unfilled value",
|
|
1810
|
+
(_name: string, content: SetupGuideContent) => {
|
|
1811
|
+
const markdown: string = getSetupGuideMarkdown(content);
|
|
1812
|
+
expect(markdown).not.toContain("## Connect");
|
|
1813
|
+
expect(markdown).not.toMatch(/^#{1,6} \d+\./m);
|
|
1814
|
+
expect(markdown).not.toContain("Full guide:");
|
|
1815
|
+
for (const bad of ["undefined", "null", "[object Object]", "NaN"]) {
|
|
1816
|
+
expect({ bad, found: proseOf(markdown).includes(bad) }).toEqual({
|
|
1817
|
+
bad,
|
|
1818
|
+
found: false,
|
|
1819
|
+
});
|
|
1820
|
+
}
|
|
1821
|
+
/*
|
|
1822
|
+
* The pieces carry no headings of their own (the card titles every
|
|
1823
|
+
* step and topic); `#` lines inside code blocks are comments.
|
|
1824
|
+
*/
|
|
1825
|
+
const pieces: Array<string> = [
|
|
1826
|
+
content.intro || "",
|
|
1827
|
+
...content.steps.map((step: SetupGuideStep): string => {
|
|
1828
|
+
return step.markdown || "";
|
|
1829
|
+
}),
|
|
1830
|
+
...[
|
|
1831
|
+
...(content.advanced || []),
|
|
1832
|
+
...(content.troubleshooting || []),
|
|
1833
|
+
].map((topic: SetupGuideTopic): string => {
|
|
1834
|
+
return topic.markdown;
|
|
1835
|
+
}),
|
|
1836
|
+
];
|
|
1837
|
+
for (const piece of pieces) {
|
|
1838
|
+
expect(proseOf(piece)).not.toMatch(/^#{1,6} /m);
|
|
1839
|
+
}
|
|
1840
|
+
},
|
|
1841
|
+
);
|
|
1842
|
+
|
|
1843
|
+
test.each(ALL_GUIDES)(
|
|
1844
|
+
"%s: links the system's docs section and the Queues page, once each",
|
|
1845
|
+
(name: string, content: SetupGuideContent) => {
|
|
1846
|
+
const system: string = name.split(" ")[0]!;
|
|
1847
|
+
const links: Array<SetupGuideLink> = content.links || [];
|
|
1848
|
+
expect(links).toEqual([
|
|
1849
|
+
{
|
|
1850
|
+
title: getMessagingSystemTitle(system),
|
|
1851
|
+
url: getMessageQueueDocsUrl(system),
|
|
1852
|
+
},
|
|
1853
|
+
{ title: "Queues documentation", url: MESSAGE_QUEUE_DOCS_PATH },
|
|
1854
|
+
]);
|
|
1855
|
+
const urls: Array<string> = links.map((link: SetupGuideLink): string => {
|
|
1856
|
+
return link.url;
|
|
1857
|
+
});
|
|
1858
|
+
expect(new Set(urls).size).toBe(urls.length);
|
|
1859
|
+
for (const link of links) {
|
|
1860
|
+
const anchor: string = link.url.split("#")[1] || "";
|
|
1861
|
+
if (anchor) {
|
|
1862
|
+
expect(DOCS_SLUGS.has(anchor)).toBe(true);
|
|
1863
|
+
}
|
|
1864
|
+
expect(link.url.startsWith(MESSAGE_QUEUE_DOCS_PATH)).toBe(true);
|
|
1865
|
+
}
|
|
1866
|
+
},
|
|
1867
|
+
);
|
|
1868
|
+
});
|
|
1869
|
+
|
|
1870
|
+
// Every key a guide sends with: `x-oneuptime-token=<key>` and `x-oneuptime-token: "<key>"`.
|
|
1871
|
+
function tokensIn(markdown: string): Array<string> {
|
|
1872
|
+
return Array.from(
|
|
1873
|
+
markdown.matchAll(/x-oneuptime-token[=:]\s*"?([^"\s]+)"?/g),
|
|
1874
|
+
).map((match: RegExpMatchArray): string => {
|
|
1875
|
+
return match[1]!;
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
describe("the ingestion key", () => {
|
|
1880
|
+
const PLACEHOLDER_VARS: GuideVariables = {
|
|
1881
|
+
oneuptimeUrl: SETUP_GUIDE_URL_PLACEHOLDER,
|
|
1882
|
+
apiKey: SETUP_GUIDE_API_KEY_PLACEHOLDER,
|
|
1883
|
+
};
|
|
1884
|
+
const PICK_KEY_NOTE: string = `Pick an ingestion key in step 1 to fill in \`${SETUP_GUIDE_API_KEY_PLACEHOLDER}\`.`;
|
|
1885
|
+
|
|
1886
|
+
test.each(SYSTEMS)(
|
|
1887
|
+
"%s shows the placeholders until a key is picked, and says where to pick one",
|
|
1888
|
+
(system: string) => {
|
|
1889
|
+
for (const content of [
|
|
1890
|
+
productContent(system, PLACEHOLDER_VARS),
|
|
1891
|
+
queueContent(sampleQueue(system), PLACEHOLDER_VARS),
|
|
1892
|
+
]) {
|
|
1893
|
+
const markdown: string = getSetupGuideMarkdown(content);
|
|
1894
|
+
expect(tokensIn(markdown).length).toBeGreaterThan(0);
|
|
1895
|
+
for (const token of tokensIn(markdown)) {
|
|
1896
|
+
expect(token).toBe(SETUP_GUIDE_API_KEY_PLACEHOLDER);
|
|
1897
|
+
}
|
|
1898
|
+
expect(markdown).toContain(`${SETUP_GUIDE_URL_PLACEHOLDER}/otlp`);
|
|
1899
|
+
expect(content.keyStep?.endpointValue).toBe(
|
|
1900
|
+
`${SETUP_GUIDE_URL_PLACEHOLDER}/otlp`,
|
|
1901
|
+
);
|
|
1902
|
+
// Said once, in the first step that carries the key.
|
|
1903
|
+
expect(content.steps[0]!.markdown).toContain(PICK_KEY_NOTE);
|
|
1904
|
+
expect(markdown.split(PICK_KEY_NOTE)).toHaveLength(2);
|
|
1905
|
+
}
|
|
1906
|
+
},
|
|
1907
|
+
);
|
|
1908
|
+
|
|
1909
|
+
test.each(ALL_GUIDES)(
|
|
1910
|
+
"%s: once a key is picked, every token in the guide is that key",
|
|
1911
|
+
(_name: string, content: SetupGuideContent) => {
|
|
1912
|
+
const markdown: string = getSetupGuideMarkdown(content);
|
|
1913
|
+
expect(markdown).not.toContain("Pick an ingestion key in step 1");
|
|
1914
|
+
const tokens: Array<string> = tokensIn(markdown);
|
|
1915
|
+
expect(tokens.length).toBeGreaterThan(0);
|
|
1916
|
+
for (const token of tokens) {
|
|
1917
|
+
expect(token).toBe(VARS.apiKey);
|
|
1918
|
+
}
|
|
1919
|
+
for (const block of getSetupGuideCodeBlocks(content)) {
|
|
1920
|
+
if (block.includes("x-oneuptime-token")) {
|
|
1921
|
+
expect(block).toContain(VARS.apiKey);
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
},
|
|
1925
|
+
);
|
|
1926
|
+
});
|
|
1927
|
+
|
|
1928
|
+
/*
|
|
1929
|
+
* The check step says what to expect, and when, in the numbers discovery
|
|
1930
|
+
* actually runs on: the interval and windows the Overview's liveness pill
|
|
1931
|
+
* states (held to the discovery job's cron by
|
|
1932
|
+
* App/Tests/Dashboard/MessageQueueOverviewWiring.test), and the span
|
|
1933
|
+
* minimum of the server's discovery, which the dashboard cannot import and
|
|
1934
|
+
* so repeats — held equal here.
|
|
1935
|
+
*/
|
|
1936
|
+
describe("the check step states discovery's own numbers", () => {
|
|
1937
|
+
test("the guide's span minimum is the server's, under the same variable", () => {
|
|
1938
|
+
expect(MESSAGE_QUEUE_GUIDE_MIN_SPANS).toBe(DEFAULT_MESSAGE_QUEUE_MIN_SPANS);
|
|
1939
|
+
expect(MESSAGE_QUEUE_MIN_SPANS_ENV_NAME).toBe(MESSAGE_QUEUE_MIN_SPANS_ENV);
|
|
1940
|
+
});
|
|
1941
|
+
|
|
1942
|
+
test("the docs page states the same numbers", () => {
|
|
1943
|
+
expect(QUEUES_DOC).toContain(
|
|
1944
|
+
`Every ${MESSAGE_QUEUE_DISCOVERY_INTERVAL_MINUTES} minutes OneUptime summarises the spans your applications sent in the last ${MESSAGE_QUEUE_DISCOVERY_WINDOW_MINUTES} minutes`,
|
|
1945
|
+
);
|
|
1946
|
+
expect(QUEUES_DOC).toContain(
|
|
1947
|
+
`number at least ${DEFAULT_MESSAGE_QUEUE_MIN_SPANS} (\`${MESSAGE_QUEUE_MIN_SPANS_ENV}\`)`,
|
|
1948
|
+
);
|
|
1949
|
+
expect(QUEUES_DOC).toContain("one broker metric is enough");
|
|
1950
|
+
expect(QUEUES_DOC).toContain(
|
|
1951
|
+
"Messaging client metrics never create a queue",
|
|
1952
|
+
);
|
|
1953
|
+
// The cloud window: "the last hour".
|
|
1954
|
+
expect(MESSAGE_QUEUE_DISCOVERY_CLOUD_METRIC_WINDOW_MINUTES).toBe(60);
|
|
1955
|
+
expect(QUEUES_DOC).toContain(
|
|
1956
|
+
"each run reads their metrics from the last hour",
|
|
1957
|
+
);
|
|
1958
|
+
});
|
|
1959
|
+
|
|
1960
|
+
test.each(SYSTEMS)("%s's product guide", (system: string) => {
|
|
1961
|
+
const content: SetupGuideContent = productContent(system);
|
|
1962
|
+
const check: SetupGuideStep = findStep(
|
|
1963
|
+
content,
|
|
1964
|
+
"Check that your queues appear",
|
|
1965
|
+
);
|
|
1966
|
+
expect(check.description).toBe(
|
|
1967
|
+
`OneUptime looks for new queues every ${MESSAGE_QUEUE_DISCOVERY_INTERVAL_MINUTES} minutes.`,
|
|
1968
|
+
);
|
|
1969
|
+
const markdown: string = check.markdown || "";
|
|
1970
|
+
expect(markdown).toContain(
|
|
1971
|
+
`Every ${MESSAGE_QUEUE_DISCOVERY_INTERVAL_MINUTES} minutes OneUptime reads the last ${MESSAGE_QUEUE_DISCOVERY_WINDOW_MINUTES} minutes of spans and lists, under **Queues**, each destination at least ${DEFAULT_MESSAGE_QUEUE_MIN_SPANS} of them name (spans of any kind but SERVER).`,
|
|
1972
|
+
);
|
|
1973
|
+
expect(markdown).toContain(
|
|
1974
|
+
`On a self-hosted OneUptime, \`${MESSAGE_QUEUE_MIN_SPANS_ENV}\` sets that number.`,
|
|
1975
|
+
);
|
|
1976
|
+
expect(markdown).toContain(
|
|
1977
|
+
"Messaging client metrics, and metrics of your own that carry `messaging.system` and `messaging.destination.name`, never create a queue: they show on the **Metrics** tab of one that exists.",
|
|
1978
|
+
);
|
|
1979
|
+
expect(markdown).toContain(
|
|
1980
|
+
`Queues are created on their own only while the project is under its [auto-create budget](${MESSAGE_QUEUE_DOCS_PATH}#the-auto-create-budget).`,
|
|
1981
|
+
);
|
|
1982
|
+
|
|
1983
|
+
// A broker metric creates a queue only where the system's metrics reach one.
|
|
1984
|
+
const reaches: boolean = canBrokerMetricsReachMessageQueue(system);
|
|
1985
|
+
expect(markdown.includes("creates its queue too")).toBe(reaches);
|
|
1986
|
+
const isCloud: boolean =
|
|
1987
|
+
descriptorOf(system).brokerMetrics.kind === "cloud-monitoring";
|
|
1988
|
+
expect(
|
|
1989
|
+
markdown.includes(
|
|
1990
|
+
`each run reads them from the last ${MESSAGE_QUEUE_DISCOVERY_CLOUD_METRIC_WINDOW_MINUTES} minutes`,
|
|
1991
|
+
),
|
|
1992
|
+
).toBe(reaches && isCloud);
|
|
1993
|
+
});
|
|
1994
|
+
|
|
1995
|
+
/*
|
|
1996
|
+
* The sentences above, held to the docs page: the variable is the
|
|
1997
|
+
* self-hosted tuning one, queues are created only under the budget, and
|
|
1998
|
+
* client metrics and the reader's own never create one.
|
|
1999
|
+
*/
|
|
2000
|
+
test("the docs page says what the product guide's check step says", () => {
|
|
2001
|
+
const tuning: string = docsSectionAt("## Self-hosted tuning");
|
|
2002
|
+
expect(tuning).toContain(
|
|
2003
|
+
"Self-hosted installations can tune discovery with these environment variables",
|
|
2004
|
+
);
|
|
2005
|
+
expect(tuning).toContain(
|
|
2006
|
+
`| \`${MESSAGE_QUEUE_MIN_SPANS_ENV}\` | \`${DEFAULT_MESSAGE_QUEUE_MIN_SPANS}\` | Spans a destination needs`,
|
|
2007
|
+
);
|
|
2008
|
+
expect(docsSectionAt("### The auto-create budget")).toContain(
|
|
2009
|
+
"Traces and broker metrics create queues on their own only while the project holds fewer than",
|
|
2010
|
+
);
|
|
2011
|
+
expect(docsSectionAt("### From messaging client metrics")).toContain(
|
|
2012
|
+
"Messaging client metrics never create a queue",
|
|
2013
|
+
);
|
|
2014
|
+
expect(docsSectionAt("### From messaging client metrics")).toContain(
|
|
2015
|
+
"A metric of your own does neither: it only shows on the queue's **Metrics** tab.",
|
|
2016
|
+
);
|
|
2017
|
+
});
|
|
2018
|
+
|
|
2019
|
+
/*
|
|
2020
|
+
* Every queue guide's check step, including the queues whose broker
|
|
2021
|
+
* metrics never reach them: an Azure queue without a namespace, a
|
|
2022
|
+
* system none of whose metrics name a queue, and a long-tail system.
|
|
2023
|
+
*/
|
|
2024
|
+
const CHECKED_QUEUES: Array<[string, MessageQueueDocumentationTarget]> = [
|
|
2025
|
+
...SYSTEMS.map(
|
|
2026
|
+
(system: string): [string, MessageQueueDocumentationTarget] => {
|
|
2027
|
+
return [system, sampleQueue(system)];
|
|
2028
|
+
},
|
|
2029
|
+
),
|
|
2030
|
+
[
|
|
2031
|
+
"servicebus without a namespace",
|
|
2032
|
+
{ system: "servicebus", destination: "orders" },
|
|
2033
|
+
],
|
|
2034
|
+
[
|
|
2035
|
+
"eventhubs without a namespace",
|
|
2036
|
+
{ system: "eventhubs", destination: "orders" },
|
|
2037
|
+
],
|
|
2038
|
+
["ibmmq", LONG_TAIL_QUEUE],
|
|
2039
|
+
];
|
|
2040
|
+
|
|
2041
|
+
test.each(CHECKED_QUEUES)(
|
|
2042
|
+
"%s's queue guide says what fills in where",
|
|
2043
|
+
(_name: string, target: MessageQueueDocumentationTarget) => {
|
|
2044
|
+
const markdown: string =
|
|
2045
|
+
findStep(queueContent(target), "Check that this queue fills in")
|
|
2046
|
+
.markdown || "";
|
|
2047
|
+
const reaches: boolean = canBrokerMetricsReachMessageQueue(
|
|
2048
|
+
target.system,
|
|
2049
|
+
target,
|
|
2050
|
+
);
|
|
2051
|
+
const isOutsideScope: boolean =
|
|
2052
|
+
canBrokerMetricsReachMessageQueue(target.system) && !reaches;
|
|
2053
|
+
|
|
2054
|
+
if (reaches) {
|
|
2055
|
+
expect(markdown).toContain(
|
|
2056
|
+
"**Broker health** on the Overview fills in from the broker's metrics as they arrive",
|
|
2057
|
+
);
|
|
2058
|
+
} else if (isOutsideScope) {
|
|
2059
|
+
expect(markdown).toContain(
|
|
2060
|
+
"Azure Monitor's metrics chart under **Broker health** on the same destination's queue in its namespace, not on this one.",
|
|
2061
|
+
);
|
|
2062
|
+
} else {
|
|
2063
|
+
// No broker metric reaches it: only metrics that name it, on its Metrics tab.
|
|
2064
|
+
expect(markdown).toContain(
|
|
2065
|
+
"Metrics that carry its `messaging.system` and `messaging.destination.name` on each datapoint show on its **Metrics** tab.",
|
|
2066
|
+
);
|
|
2067
|
+
expect(markdown).not.toContain("Broker health");
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
/*
|
|
2071
|
+
* Liveness: broker metrics, and the cloud monitoring window they are
|
|
2072
|
+
* read over, only where they reach this queue; otherwise what does
|
|
2073
|
+
* sight it — spans and messaging client metrics.
|
|
2074
|
+
*/
|
|
2075
|
+
const isCloud: boolean =
|
|
2076
|
+
getMessagingBrokerMetricsSource(target.system).kind ===
|
|
2077
|
+
"cloud-monitoring";
|
|
2078
|
+
expect(markdown).toContain(
|
|
2079
|
+
reaches
|
|
2080
|
+
? `The header reads **Seen recently** once discovery finds spans or broker metrics naming this queue. It runs every ${MESSAGE_QUEUE_DISCOVERY_INTERVAL_MINUTES} minutes over the last ${MESSAGE_QUEUE_DISCOVERY_WINDOW_MINUTES} minutes of telemetry${
|
|
2081
|
+
isCloud
|
|
2082
|
+
? `, and over the last ${MESSAGE_QUEUE_DISCOVERY_CLOUD_METRIC_WINDOW_MINUTES} minutes of cloud monitoring metrics, which arrive late`
|
|
2083
|
+
: ""
|
|
2084
|
+
}.`
|
|
2085
|
+
: `The header reads **Seen recently** once discovery finds spans or messaging client metrics naming this queue. It runs every ${MESSAGE_QUEUE_DISCOVERY_INTERVAL_MINUTES} minutes over the last ${MESSAGE_QUEUE_DISCOVERY_WINDOW_MINUTES} minutes of telemetry.`,
|
|
2086
|
+
);
|
|
2087
|
+
if (!reaches) {
|
|
2088
|
+
expect(markdown).not.toContain("broker metrics naming this queue");
|
|
2089
|
+
expect(markdown).not.toContain("cloud monitoring metrics");
|
|
2090
|
+
}
|
|
2091
|
+
},
|
|
2092
|
+
);
|
|
2093
|
+
|
|
2094
|
+
test("the docs page says client metrics keep a queue's Last seen current", () => {
|
|
2095
|
+
expect(docsSectionAt("### From messaging client metrics")).toContain(
|
|
2096
|
+
"A client metric that names a queue that exists keeps its **Last seen** current, as its spans do.",
|
|
2097
|
+
);
|
|
2098
|
+
expect(docsSectionAt("### Lifecycle and archiving")).toContain(
|
|
2099
|
+
"**Last seen** is when discovery last saw the queue, in traces, messaging client metrics or broker metrics.",
|
|
2100
|
+
);
|
|
2101
|
+
});
|
|
2102
|
+
|
|
2103
|
+
test.each(SYSTEMS)("%s's queue guide", (system: string) => {
|
|
2104
|
+
const target: MessageQueueDocumentationTarget = sampleQueue(system);
|
|
2105
|
+
const check: SetupGuideStep = findStep(
|
|
2106
|
+
queueContent(target),
|
|
2107
|
+
"Check that this queue fills in",
|
|
2108
|
+
);
|
|
2109
|
+
const markdown: string = check.markdown || "";
|
|
2110
|
+
// What spans fill in, and that they are matched as they are ingested.
|
|
2111
|
+
expect(markdown).toContain(
|
|
2112
|
+
"Every span that names this queue is tagged with it as OneUptime ingests it",
|
|
2113
|
+
);
|
|
2114
|
+
expect(markdown).toContain("**Producers** and **Consumers**");
|
|
2115
|
+
expect(markdown).toContain("the **Traces** tab");
|
|
2116
|
+
// A queue that exists needs no minimum: spans always count towards it.
|
|
2117
|
+
expect(markdown).not.toContain(
|
|
2118
|
+
`at least ${DEFAULT_MESSAGE_QUEUE_MIN_SPANS}`,
|
|
2119
|
+
);
|
|
2120
|
+
expect(
|
|
2121
|
+
markdown.includes("**Broker health** on the Overview fills in"),
|
|
2122
|
+
).toBe(canBrokerMetricsReachMessageQueue(system, target));
|
|
2123
|
+
});
|
|
2124
|
+
});
|
|
2125
|
+
|
|
2126
|
+
/*
|
|
2127
|
+
* Troubleshooting is the docs page's "## Troubleshooting", condensed: the
|
|
2128
|
+
* same entries in the same order, each linking to its own, and every fact
|
|
2129
|
+
* it states one the docs entry states too.
|
|
2130
|
+
*/
|
|
2131
|
+
describe("troubleshooting follows the docs page", () => {
|
|
2132
|
+
const TROUBLESHOOTING: string = docsSectionAt("## Troubleshooting");
|
|
2133
|
+
const DOCS_ENTRIES: Array<string> = Array.from(
|
|
2134
|
+
TROUBLESHOOTING.matchAll(/^### (.+)$/gm),
|
|
2135
|
+
).map((match: RegExpMatchArray): string => {
|
|
2136
|
+
return match[1]!.trim();
|
|
2137
|
+
});
|
|
2138
|
+
|
|
2139
|
+
// The docs entry under a "### " heading of the troubleshooting section.
|
|
2140
|
+
function docsEntry(title: string): string {
|
|
2141
|
+
const start: number = TROUBLESHOOTING.indexOf(`### ${title}\n`);
|
|
2142
|
+
expect(start).toBeGreaterThan(-1);
|
|
2143
|
+
const rest: string = TROUBLESHOOTING.slice(start + title.length + 5);
|
|
2144
|
+
const end: number = rest.search(/\n### /);
|
|
2145
|
+
return (end === -1 ? rest : rest.slice(0, end)).toLowerCase();
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
test("the docs page has the four entries", () => {
|
|
2149
|
+
expect(DOCS_ENTRIES).toEqual([
|
|
2150
|
+
"A queue my applications use was not created",
|
|
2151
|
+
"Broker health stays empty",
|
|
2152
|
+
"Two queues for one destination",
|
|
2153
|
+
"A new queue for every request",
|
|
2154
|
+
]);
|
|
2155
|
+
});
|
|
2156
|
+
|
|
2157
|
+
test.each(ALL_GUIDES)(
|
|
2158
|
+
"%s: the same entries, in order, each linking to its own",
|
|
2159
|
+
(_name: string, content: SetupGuideContent) => {
|
|
2160
|
+
expect(topicTitles(content.troubleshooting)).toEqual(DOCS_ENTRIES);
|
|
2161
|
+
for (const topic of content.troubleshooting || []) {
|
|
2162
|
+
const anchor: string = slugify(topic.title);
|
|
2163
|
+
expect(DOCS_SLUGS.has(anchor)).toBe(true);
|
|
2164
|
+
expect(topic.markdown).toContain(
|
|
2165
|
+
`[${topic.title}](${MESSAGE_QUEUE_DOCS_PATH}#${anchor})`,
|
|
2166
|
+
);
|
|
2167
|
+
}
|
|
2168
|
+
},
|
|
2169
|
+
);
|
|
2170
|
+
|
|
2171
|
+
/*
|
|
2172
|
+
* Phrases each guide entry states, which the docs entry must state too
|
|
2173
|
+
* (compared without regard to case).
|
|
2174
|
+
*/
|
|
2175
|
+
const SHARED_FACTS: ReadonlyArray<[string, ReadonlyArray<string>]> = [
|
|
2176
|
+
[
|
|
2177
|
+
"A queue my applications use was not created",
|
|
2178
|
+
[
|
|
2179
|
+
"Celery's spans, BullMQ's own telemetry",
|
|
2180
|
+
"Add `messaging.system` and `messaging.destination.name`, and give the spans the PRODUCER or CONSUMER kind.",
|
|
2181
|
+
"or only SERVER spans name it",
|
|
2182
|
+
`Fewer than ${DEFAULT_MESSAGE_QUEUE_MIN_SPANS} of its spans arrived in the last ${MESSAGE_QUEUE_DISCOVERY_WINDOW_MINUTES} minutes, or the project reached its [auto-create budget]`,
|
|
2183
|
+
"When its spans do name it, create it by hand with the same system and destination (**Queues → Create Queue**): its **Traces** tab fills in from the spans already stored.",
|
|
2184
|
+
],
|
|
2185
|
+
],
|
|
2186
|
+
[
|
|
2187
|
+
"Broker health stays empty",
|
|
2188
|
+
[
|
|
2189
|
+
"Check the collector's log. OneUptime refuses a wrong ingestion key with `401` (`422` for a disabled key or a browser key), and the collector logs `Exporting failed` for every batch it drops.",
|
|
2190
|
+
],
|
|
2191
|
+
],
|
|
2192
|
+
[
|
|
2193
|
+
"Two queues for one destination",
|
|
2194
|
+
[
|
|
2195
|
+
"Two systems name it: a Kafka client on Event Hubs' Kafka endpoint reports `kafka`, the Event Hubs SDK `eventhubs`.",
|
|
2196
|
+
"One Service Bus or Event Hubs sighting has no namespace: the emulator or a custom domain.",
|
|
2197
|
+
"RabbitMQ spans usually name the exchange, and the broker's metrics the queue: two queues in OneUptime unless the exchange and the queue share a name",
|
|
2198
|
+
"Archive the one you do not want: an archived queue stays archived, and what names it no longer creates a new one.",
|
|
2199
|
+
],
|
|
2200
|
+
],
|
|
2201
|
+
[
|
|
2202
|
+
"A new queue for every request",
|
|
2203
|
+
[
|
|
2204
|
+
"A destination named per request or per consumer that OneUptime does not recognise — a numeric suffix, a hash — makes a queue per name. UUIDs are already folded",
|
|
2205
|
+
"Mark such destinations `messaging.destination.temporary=true` in your instrumentation, or give them a stable name, then archive the queues already created.",
|
|
2206
|
+
],
|
|
2207
|
+
],
|
|
2208
|
+
];
|
|
2209
|
+
|
|
2210
|
+
test.each(ALL_GUIDES)(
|
|
2211
|
+
"%s: states the docs entries' facts",
|
|
2212
|
+
(_name: string, content: SetupGuideContent) => {
|
|
2213
|
+
for (const [title, facts] of SHARED_FACTS) {
|
|
2214
|
+
const guide: string = findTopic(
|
|
2215
|
+
content.troubleshooting,
|
|
2216
|
+
title,
|
|
2217
|
+
).markdown.toLowerCase();
|
|
2218
|
+
const docs: string = docsEntry(title);
|
|
2219
|
+
for (const fact of facts) {
|
|
2220
|
+
const lower: string = fact.toLowerCase();
|
|
2221
|
+
expect({ title, fact, inGuide: guide.includes(lower) }).toEqual({
|
|
2222
|
+
title,
|
|
2223
|
+
fact,
|
|
2224
|
+
inGuide: true,
|
|
2225
|
+
});
|
|
2226
|
+
expect({ title, fact, inDocs: docs.includes(lower) }).toEqual({
|
|
2227
|
+
title,
|
|
2228
|
+
fact,
|
|
2229
|
+
inDocs: true,
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
},
|
|
2234
|
+
);
|
|
2235
|
+
|
|
2236
|
+
/*
|
|
2237
|
+
* Broker health's causes, narrowed to the system: each phrase is in the
|
|
2238
|
+
* system's guide entry and in the docs entry.
|
|
2239
|
+
*/
|
|
2240
|
+
const SYSTEM_FACTS: ReadonlyArray<[string, ReadonlyArray<string>]> = [
|
|
2241
|
+
[
|
|
2242
|
+
"kafka",
|
|
2243
|
+
["reports a consumer group's lag only after the group commits an offset"],
|
|
2244
|
+
],
|
|
2245
|
+
[
|
|
2246
|
+
"rabbitmq",
|
|
2247
|
+
[
|
|
2248
|
+
"RabbitMQ spans usually name the exchange",
|
|
2249
|
+
"reports only the queues of virtual hosts its user has access to, and logs no error for the rest",
|
|
2250
|
+
"RabbitMQ's counters appear with the first activity",
|
|
2251
|
+
],
|
|
2252
|
+
],
|
|
2253
|
+
[
|
|
2254
|
+
"servicebus",
|
|
2255
|
+
[
|
|
2256
|
+
"returns 10 queues per metric and namespace unless `maximum_number_of_records_per_resource` is raised",
|
|
2257
|
+
],
|
|
2258
|
+
],
|
|
2259
|
+
[
|
|
2260
|
+
"eventhubs",
|
|
2261
|
+
[
|
|
2262
|
+
"returns 10 queues per metric and namespace unless `maximum_number_of_records_per_resource` is raised",
|
|
2263
|
+
],
|
|
2264
|
+
],
|
|
2265
|
+
[
|
|
2266
|
+
"aws_sqs",
|
|
2267
|
+
[
|
|
2268
|
+
"`aws_cloudwatch` waits `delay` (10 minutes) for CloudWatch to publish, so its points arrive 11 to 17 minutes late",
|
|
2269
|
+
],
|
|
2270
|
+
],
|
|
2271
|
+
[
|
|
2272
|
+
"aws.sns",
|
|
2273
|
+
[
|
|
2274
|
+
"`aws_cloudwatch` waits `delay` (10 minutes) for CloudWatch to publish, so its points arrive 11 to 17 minutes late",
|
|
2275
|
+
],
|
|
2276
|
+
],
|
|
2277
|
+
[
|
|
2278
|
+
"gcp_pubsub",
|
|
2279
|
+
[
|
|
2280
|
+
"topic has the topic metrics and its subscriptions the subscription metrics",
|
|
2281
|
+
"Cloud Monitoring reads only the types in `metrics_list`",
|
|
2282
|
+
"Pub/Sub's metrics arrive minutes late",
|
|
2283
|
+
],
|
|
2284
|
+
],
|
|
2285
|
+
];
|
|
2286
|
+
|
|
2287
|
+
test.each(SYSTEM_FACTS)(
|
|
2288
|
+
"%s's Broker health entry names its own causes, as the docs entry does",
|
|
2289
|
+
(system: string, facts: ReadonlyArray<string>) => {
|
|
2290
|
+
const guide: string = findTopic(
|
|
2291
|
+
productContent(system).troubleshooting,
|
|
2292
|
+
"Broker health stays empty",
|
|
2293
|
+
).markdown.toLowerCase();
|
|
2294
|
+
const docs: string = docsEntry("Broker health stays empty");
|
|
2295
|
+
for (const fact of facts) {
|
|
2296
|
+
const lower: string = fact.toLowerCase();
|
|
2297
|
+
expect({ system, fact, inGuide: guide.includes(lower) }).toEqual({
|
|
2298
|
+
system,
|
|
2299
|
+
fact,
|
|
2300
|
+
inGuide: true,
|
|
2301
|
+
});
|
|
2302
|
+
expect({ system, fact, inDocs: docs.includes(lower) }).toEqual({
|
|
2303
|
+
system,
|
|
2304
|
+
fact,
|
|
2305
|
+
inDocs: true,
|
|
2306
|
+
});
|
|
2307
|
+
}
|
|
2308
|
+
},
|
|
2309
|
+
);
|
|
2310
|
+
|
|
2311
|
+
test("a system whose broker metrics chart nowhere says so, as the docs entry does", () => {
|
|
2312
|
+
const docs: string = docsEntry("Broker health stays empty");
|
|
2313
|
+
expect(docs).toContain(
|
|
2314
|
+
"the system has no charted metrics: azure event grid, nats, jms brokers other than activemq and bullmq",
|
|
2315
|
+
);
|
|
2316
|
+
expect(docs).toContain("their metrics are in the **metrics** explorer.");
|
|
2317
|
+
for (const system of ["nats", "eventgrid", "bullmq"]) {
|
|
2318
|
+
expect(
|
|
2319
|
+
findTopic(
|
|
2320
|
+
productContent(system).troubleshooting,
|
|
2321
|
+
"Broker health stays empty",
|
|
2322
|
+
).markdown,
|
|
2323
|
+
).toContain(
|
|
2324
|
+
`- ${descriptorOf(system).displayName} has no charted metrics. Its metrics are in the **Metrics** explorer.`,
|
|
2325
|
+
);
|
|
2326
|
+
}
|
|
2327
|
+
});
|
|
2328
|
+
|
|
2329
|
+
/*
|
|
2330
|
+
* JMS keeps the docs entry's exception: an ActiveMQ broker's metrics DO
|
|
2331
|
+
* chart (they turn the queue into an ActiveMQ one), as the guide's own
|
|
2332
|
+
* ActiveMQ and charted-metrics topics say.
|
|
2333
|
+
*/
|
|
2334
|
+
test("JMS names only the brokers other than ActiveMQ, and points at the ActiveMQ topic", () => {
|
|
2335
|
+
for (const content of [
|
|
2336
|
+
productContent("jms"),
|
|
2337
|
+
queueContent(sampleQueue("jms")),
|
|
2338
|
+
]) {
|
|
2339
|
+
const markdown: string = findTopic(
|
|
2340
|
+
content.troubleshooting,
|
|
2341
|
+
"Broker health stays empty",
|
|
2342
|
+
).markdown;
|
|
2343
|
+
expect(markdown).toContain(
|
|
2344
|
+
"- JMS brokers other than ActiveMQ have no charted metrics. Their metrics are in the **Metrics** explorer. For an ActiveMQ broker, see **If the broker is Apache ActiveMQ** under **Advanced**.",
|
|
2345
|
+
);
|
|
2346
|
+
expect(markdown).not.toContain("JMS has no charted metrics");
|
|
2347
|
+
findTopic(content.advanced, "If the broker is Apache ActiveMQ");
|
|
2348
|
+
}
|
|
2349
|
+
});
|
|
2350
|
+
|
|
2351
|
+
/*
|
|
2352
|
+
* Every guide the dashboard can show, plus the queues whose Broker
|
|
2353
|
+
* health entry differs: Azure queues without a namespace, and a queue
|
|
2354
|
+
* with no system at all.
|
|
2355
|
+
*/
|
|
2356
|
+
const TROUBLESHOOTING_GUIDES: Array<[string, SetupGuideContent]> = [
|
|
2357
|
+
...ALL_GUIDES,
|
|
2358
|
+
[
|
|
2359
|
+
"servicebus (queue without a namespace)",
|
|
2360
|
+
queueContent({ system: "servicebus", destination: "orders" }),
|
|
2361
|
+
],
|
|
2362
|
+
[
|
|
2363
|
+
"eventhubs (queue without a namespace)",
|
|
2364
|
+
queueContent({ system: "eventhubs", destination: "orders" }),
|
|
2365
|
+
],
|
|
2366
|
+
["no system (queue)", queueContent({ system: "", destination: "orders" })],
|
|
2367
|
+
];
|
|
2368
|
+
|
|
2369
|
+
/*
|
|
2370
|
+
* The sentences of a troubleshooting topic or docs entry, compared
|
|
2371
|
+
* without regard to case: links into the docs page as the page writes
|
|
2372
|
+
* them, "(see [..](..))" asides dropped (the guide links its entry
|
|
2373
|
+
* instead), split at each sentence end and semicolon, without the
|
|
2374
|
+
* guide's closing "See [entry] on the Queues documentation page." line.
|
|
2375
|
+
*/
|
|
2376
|
+
function normalizeEntryText(text: string): string {
|
|
2377
|
+
return text
|
|
2378
|
+
.replace(/\(\/docs\/telemetry\/queues#/g, "(#")
|
|
2379
|
+
.replace(/\s*\(see \[[^\]]*\]\([^)]*\)\)/gi, "")
|
|
2380
|
+
.replace(/\s+/g, " ")
|
|
2381
|
+
.toLowerCase()
|
|
2382
|
+
.trim();
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
const ENTRY_LINK_LINE: RegExp =
|
|
2386
|
+
/^See \[[^\]]+\]\([^)]+\) on the Queues documentation page\.$/;
|
|
2387
|
+
|
|
2388
|
+
function entrySentences(markdown: string): Array<string> {
|
|
2389
|
+
const sentences: Array<string> = [];
|
|
2390
|
+
for (const rawLine of markdown.split("\n")) {
|
|
2391
|
+
const line: string = rawLine.replace(/^- /, "").trim();
|
|
2392
|
+
if (!line || ENTRY_LINK_LINE.test(line)) {
|
|
2393
|
+
continue;
|
|
2394
|
+
}
|
|
2395
|
+
for (const piece of normalizeEntryText(line).split(/(?<=[.;])\s+/)) {
|
|
2396
|
+
const sentence: string = piece.replace(/[.;]$/, "").trim();
|
|
2397
|
+
if (sentence) {
|
|
2398
|
+
sentences.push(sentence);
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
return sentences;
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
/*
|
|
2406
|
+
* The only sentences a guide's troubleshooting may state that its docs
|
|
2407
|
+
* entry does not: each proved by the docs section it comes from, by the
|
|
2408
|
+
* step of the same guide it points at, or by the Advanced topic it names.
|
|
2409
|
+
*/
|
|
2410
|
+
interface TroubleshootingAddition {
|
|
2411
|
+
sentence: string;
|
|
2412
|
+
docs?: { section: string; says: string };
|
|
2413
|
+
step?: { number: number; contains: string };
|
|
2414
|
+
topic?: string;
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
const NO_CHARTED_METRICS: { section: string; says: string } = {
|
|
2418
|
+
section: "## Troubleshooting",
|
|
2419
|
+
says: "The system has no charted metrics: Azure Event Grid, NATS, JMS brokers other than ActiveMQ and BullMQ",
|
|
2420
|
+
};
|
|
2421
|
+
|
|
2422
|
+
const ADDITIONS: ReadonlyArray<TroubleshootingAddition> = [
|
|
2423
|
+
{
|
|
2424
|
+
sentence: "for bullmq, the transform in step 2 does that",
|
|
2425
|
+
step: { number: 2, contains: "transform/bullmq:" },
|
|
2426
|
+
},
|
|
2427
|
+
{
|
|
2428
|
+
sentence: "the receiver skips topics whose names start with `_`",
|
|
2429
|
+
docs: {
|
|
2430
|
+
section: "### Apache Kafka",
|
|
2431
|
+
says: "skips topics whose names start with `_`",
|
|
2432
|
+
},
|
|
2433
|
+
},
|
|
2434
|
+
{
|
|
2435
|
+
sentence:
|
|
2436
|
+
"the two meet for messages sent through the default exchange, whose routing key is the queue's name",
|
|
2437
|
+
docs: {
|
|
2438
|
+
section: "## Limitations",
|
|
2439
|
+
says: "The two meet for messages sent through the default exchange, whose routing key is the queue's name",
|
|
2440
|
+
},
|
|
2441
|
+
},
|
|
2442
|
+
{
|
|
2443
|
+
sentence: "the config in step 3 raises it",
|
|
2444
|
+
step: {
|
|
2445
|
+
number: 3,
|
|
2446
|
+
contains: "maximum_number_of_records_per_resource: 1000",
|
|
2447
|
+
},
|
|
2448
|
+
},
|
|
2449
|
+
{
|
|
2450
|
+
sentence:
|
|
2451
|
+
"the receiver lists your namespaces once a day, so a new namespace can take up to 24 hours to appear",
|
|
2452
|
+
docs: {
|
|
2453
|
+
section: "### Azure Service Bus",
|
|
2454
|
+
says: "The receiver lists your namespaces once a day, so a new namespace can take up to 24 hours to appear.",
|
|
2455
|
+
},
|
|
2456
|
+
},
|
|
2457
|
+
{
|
|
2458
|
+
sentence: "only the broker's own metrics need a system oneuptime knows",
|
|
2459
|
+
docs: {
|
|
2460
|
+
section: "## Supported messaging systems",
|
|
2461
|
+
says: "only the broker's own metrics need a system from this table",
|
|
2462
|
+
},
|
|
2463
|
+
},
|
|
2464
|
+
{
|
|
2465
|
+
sentence: "its metrics are in the **metrics** explorer",
|
|
2466
|
+
docs: {
|
|
2467
|
+
section: "## Troubleshooting",
|
|
2468
|
+
says: "Their metrics are in the **Metrics** explorer.",
|
|
2469
|
+
},
|
|
2470
|
+
},
|
|
2471
|
+
{
|
|
2472
|
+
sentence: "jms brokers other than activemq have no charted metrics",
|
|
2473
|
+
docs: NO_CHARTED_METRICS,
|
|
2474
|
+
},
|
|
2475
|
+
{ sentence: "nats has no charted metrics", docs: NO_CHARTED_METRICS },
|
|
2476
|
+
{
|
|
2477
|
+
sentence: "azure event grid has no charted metrics",
|
|
2478
|
+
docs: NO_CHARTED_METRICS,
|
|
2479
|
+
},
|
|
2480
|
+
{ sentence: "bullmq has no charted metrics", docs: NO_CHARTED_METRICS },
|
|
2481
|
+
{
|
|
2482
|
+
sentence:
|
|
2483
|
+
"for an activemq broker, see **if the broker is apache activemq** under **advanced**",
|
|
2484
|
+
topic: "If the broker is Apache ActiveMQ",
|
|
2485
|
+
},
|
|
2486
|
+
];
|
|
2487
|
+
|
|
2488
|
+
test.each(TROUBLESHOOTING_GUIDES)(
|
|
2489
|
+
"%s: every sentence of every entry is its docs entry's, or a proved addition",
|
|
2490
|
+
(_name: string, content: SetupGuideContent) => {
|
|
2491
|
+
for (const topic of content.troubleshooting || []) {
|
|
2492
|
+
const docs: string = normalizeEntryText(docsEntry(topic.title));
|
|
2493
|
+
for (const sentence of entrySentences(topic.markdown)) {
|
|
2494
|
+
if (docs.includes(sentence)) {
|
|
2495
|
+
continue;
|
|
2496
|
+
}
|
|
2497
|
+
const addition: TroubleshootingAddition | undefined = ADDITIONS.find(
|
|
2498
|
+
(candidate: TroubleshootingAddition): boolean => {
|
|
2499
|
+
return candidate.sentence === sentence;
|
|
2500
|
+
},
|
|
2501
|
+
);
|
|
2502
|
+
expect({
|
|
2503
|
+
title: topic.title,
|
|
2504
|
+
sentence,
|
|
2505
|
+
sourced: Boolean(addition),
|
|
2506
|
+
}).toEqual({ title: topic.title, sentence, sourced: true });
|
|
2507
|
+
if (addition?.step) {
|
|
2508
|
+
const step: SetupGuideStep | undefined =
|
|
2509
|
+
content.steps[addition.step.number - 2];
|
|
2510
|
+
expect({
|
|
2511
|
+
sentence,
|
|
2512
|
+
step: (step?.markdown || "").includes(addition.step.contains),
|
|
2513
|
+
}).toEqual({ sentence, step: true });
|
|
2514
|
+
}
|
|
2515
|
+
if (addition?.topic) {
|
|
2516
|
+
findTopic(content.advanced, addition.topic);
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
},
|
|
2521
|
+
);
|
|
2522
|
+
|
|
2523
|
+
test.each(
|
|
2524
|
+
ADDITIONS.map(
|
|
2525
|
+
(
|
|
2526
|
+
addition: TroubleshootingAddition,
|
|
2527
|
+
): [string, TroubleshootingAddition] => {
|
|
2528
|
+
return [addition.sentence, addition];
|
|
2529
|
+
},
|
|
2530
|
+
),
|
|
2531
|
+
)(
|
|
2532
|
+
'the addition "%s" is proved, and some guide states it',
|
|
2533
|
+
(sentence: string, addition: TroubleshootingAddition) => {
|
|
2534
|
+
expect(
|
|
2535
|
+
Boolean(addition.docs) ||
|
|
2536
|
+
Boolean(addition.step) ||
|
|
2537
|
+
Boolean(addition.topic),
|
|
2538
|
+
).toBe(true);
|
|
2539
|
+
if (addition.docs) {
|
|
2540
|
+
expect(
|
|
2541
|
+
normalizeEntryText(docsSectionAt(addition.docs.section)),
|
|
2542
|
+
).toContain(normalizeEntryText(addition.docs.says));
|
|
2543
|
+
}
|
|
2544
|
+
const stated: boolean = TROUBLESHOOTING_GUIDES.some(
|
|
2545
|
+
([, content]: [string, SetupGuideContent]): boolean => {
|
|
2546
|
+
return (content.troubleshooting || []).some(
|
|
2547
|
+
(topic: SetupGuideTopic): boolean => {
|
|
2548
|
+
return entrySentences(topic.markdown).includes(sentence);
|
|
2549
|
+
},
|
|
2550
|
+
);
|
|
2551
|
+
},
|
|
2552
|
+
);
|
|
2553
|
+
expect(stated).toBe(true);
|
|
2554
|
+
},
|
|
2555
|
+
);
|
|
2556
|
+
|
|
2557
|
+
/*
|
|
2558
|
+
* Every "step N" a guide's text names: the pick-key note's step 1, or a
|
|
2559
|
+
* step of the same guide holding what the sentence says it holds.
|
|
2560
|
+
*/
|
|
2561
|
+
const STEP_REFERENCES: ReadonlyArray<[RegExp, string]> = [
|
|
2562
|
+
[/Pick an ingestion key in step (\d+)/g, ""],
|
|
2563
|
+
[/the transform in step (\d+)/gi, "transform/bullmq:"],
|
|
2564
|
+
[
|
|
2565
|
+
/the config in step (\d+) raises it/gi,
|
|
2566
|
+
"maximum_number_of_records_per_resource:",
|
|
2567
|
+
],
|
|
2568
|
+
[/with the configuration in step (\d+), CloudWatch's/g, "aws_cloudwatch/"],
|
|
2569
|
+
];
|
|
2570
|
+
|
|
2571
|
+
test.each(TROUBLESHOOTING_GUIDES)(
|
|
2572
|
+
"%s: every step a sentence names is the step that holds it",
|
|
2573
|
+
(name: string, content: SetupGuideContent) => {
|
|
2574
|
+
const pieces: Array<string> = [
|
|
2575
|
+
content.intro || "",
|
|
2576
|
+
...content.steps.map((step: SetupGuideStep): string => {
|
|
2577
|
+
return step.markdown || "";
|
|
2578
|
+
}),
|
|
2579
|
+
...[
|
|
2580
|
+
...(content.advanced || []),
|
|
2581
|
+
...(content.troubleshooting || []),
|
|
2582
|
+
].map((topic: SetupGuideTopic): string => {
|
|
2583
|
+
return topic.markdown;
|
|
2584
|
+
}),
|
|
2585
|
+
];
|
|
2586
|
+
const prose: string = pieces.map(proseOf).join("\n");
|
|
2587
|
+
let checked: number = 0;
|
|
2588
|
+
for (const [pattern, contains] of STEP_REFERENCES) {
|
|
2589
|
+
for (const match of prose.matchAll(pattern)) {
|
|
2590
|
+
checked += 1;
|
|
2591
|
+
const number: number = Number(match[1]);
|
|
2592
|
+
if (contains === "") {
|
|
2593
|
+
expect(number).toBe(1);
|
|
2594
|
+
continue;
|
|
2595
|
+
}
|
|
2596
|
+
const step: SetupGuideStep | undefined = content.steps[number - 2];
|
|
2597
|
+
expect({
|
|
2598
|
+
name,
|
|
2599
|
+
reference: match[0],
|
|
2600
|
+
holds: (step?.markdown || "").includes(contains),
|
|
2601
|
+
}).toEqual({ name, reference: match[0], holds: true });
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
// No mention of a step that the table above does not check.
|
|
2605
|
+
expect({
|
|
2606
|
+
name,
|
|
2607
|
+
mentions: Array.from(prose.matchAll(/\bstep \d+\b/gi)).length,
|
|
2608
|
+
}).toEqual({ name, mentions: checked });
|
|
2609
|
+
},
|
|
2610
|
+
);
|
|
2611
|
+
|
|
2612
|
+
/*
|
|
2613
|
+
* The Broker health topic's folded summary names this system's causes
|
|
2614
|
+
* and no other: every cause it lists is one the bullets below it state.
|
|
2615
|
+
*/
|
|
2616
|
+
const SUMMARY_CAUSES: ReadonlyArray<
|
|
2617
|
+
[RegExp, (cause: RegExpMatchArray) => string]
|
|
2618
|
+
> = [
|
|
2619
|
+
[
|
|
2620
|
+
/^a refused key$/,
|
|
2621
|
+
(): string => {
|
|
2622
|
+
return "refuses a wrong ingestion key";
|
|
2623
|
+
},
|
|
2624
|
+
],
|
|
2625
|
+
[
|
|
2626
|
+
/^spans without a namespace( on a different queue from azure monitor's metrics)?$/,
|
|
2627
|
+
(): string => {
|
|
2628
|
+
return "spans without a namespace host are on a different queue from azure monitor's metrics";
|
|
2629
|
+
},
|
|
2630
|
+
],
|
|
2631
|
+
[
|
|
2632
|
+
/^a system oneuptime does not know$/,
|
|
2633
|
+
(): string => {
|
|
2634
|
+
return "only the broker's own metrics need a system oneuptime knows";
|
|
2635
|
+
},
|
|
2636
|
+
],
|
|
2637
|
+
[
|
|
2638
|
+
/^no charted metrics for (.+)$/,
|
|
2639
|
+
(cause: RegExpMatchArray): string => {
|
|
2640
|
+
return `${cause[1]} ha${cause[1]!.startsWith("jms brokers") ? "ve" : "s"} no charted metrics`;
|
|
2641
|
+
},
|
|
2642
|
+
],
|
|
2643
|
+
[
|
|
2644
|
+
/^a consumer group that has not committed an offset$/,
|
|
2645
|
+
(): string => {
|
|
2646
|
+
return "reports a consumer group's lag only after the group commits an offset";
|
|
2647
|
+
},
|
|
2648
|
+
],
|
|
2649
|
+
[
|
|
2650
|
+
/^a topic the receiver skips$/,
|
|
2651
|
+
(): string => {
|
|
2652
|
+
return "the receiver skips topics whose names start with";
|
|
2653
|
+
},
|
|
2654
|
+
],
|
|
2655
|
+
[
|
|
2656
|
+
/^spans that name the exchange rather than the queue$/,
|
|
2657
|
+
(): string => {
|
|
2658
|
+
return "rabbitmq spans usually name the exchange, while the broker's metrics describe queues";
|
|
2659
|
+
},
|
|
2660
|
+
],
|
|
2661
|
+
[
|
|
2662
|
+
/^a virtual host the user has no access to$/,
|
|
2663
|
+
(): string => {
|
|
2664
|
+
return "reports only the queues of virtual hosts its user has access to";
|
|
2665
|
+
},
|
|
2666
|
+
],
|
|
2667
|
+
[
|
|
2668
|
+
/^more queues than azure monitor returns$/,
|
|
2669
|
+
(): string => {
|
|
2670
|
+
return "azure monitor returns 10 queues per metric and namespace";
|
|
2671
|
+
},
|
|
2672
|
+
],
|
|
2673
|
+
[
|
|
2674
|
+
/^a namespace not listed yet$/,
|
|
2675
|
+
(): string => {
|
|
2676
|
+
return "the receiver lists your namespaces once a day";
|
|
2677
|
+
},
|
|
2678
|
+
],
|
|
2679
|
+
[
|
|
2680
|
+
/^metrics that arrive (.+) late$/,
|
|
2681
|
+
(cause: RegExpMatchArray): string => {
|
|
2682
|
+
return `so its points arrive ${cause[1]} late`;
|
|
2683
|
+
},
|
|
2684
|
+
],
|
|
2685
|
+
[
|
|
2686
|
+
/^a topic and its subscriptions on separate queues$/,
|
|
2687
|
+
(): string => {
|
|
2688
|
+
return "a pub/sub topic has the topic metrics and its subscriptions the subscription metrics";
|
|
2689
|
+
},
|
|
2690
|
+
],
|
|
2691
|
+
[
|
|
2692
|
+
/^a metric type the config does not list$/,
|
|
2693
|
+
(): string => {
|
|
2694
|
+
return "cloud monitoring reads only the types in `metrics_list`";
|
|
2695
|
+
},
|
|
2696
|
+
],
|
|
2697
|
+
[
|
|
2698
|
+
/^late metrics$/,
|
|
2699
|
+
(): string => {
|
|
2700
|
+
return "pub/sub's metrics arrive minutes late";
|
|
2701
|
+
},
|
|
2702
|
+
],
|
|
2703
|
+
[
|
|
2704
|
+
/^a metric that names the queue differently from the spans$/,
|
|
2705
|
+
(): string => {
|
|
2706
|
+
return "the metric names the queue differently from the spans";
|
|
2707
|
+
},
|
|
2708
|
+
],
|
|
2709
|
+
];
|
|
2710
|
+
|
|
2711
|
+
test.each(TROUBLESHOOTING_GUIDES)(
|
|
2712
|
+
"%s: Broker health's summary names only causes its bullets state",
|
|
2713
|
+
(name: string, content: SetupGuideContent) => {
|
|
2714
|
+
const topic: SetupGuideTopic = findTopic(
|
|
2715
|
+
content.troubleshooting,
|
|
2716
|
+
"Broker health stays empty",
|
|
2717
|
+
);
|
|
2718
|
+
const body: string = normalizeEntryText(topic.markdown);
|
|
2719
|
+
const causes: Array<string> = (topic.summary || "")
|
|
2720
|
+
.toLowerCase()
|
|
2721
|
+
.replace(/\.$/, "")
|
|
2722
|
+
.split(/, (?:or )?/);
|
|
2723
|
+
expect(causes[0]).toBe("a refused key");
|
|
2724
|
+
expect(causes.length).toBeGreaterThan(1);
|
|
2725
|
+
for (const cause of causes) {
|
|
2726
|
+
const known: Array<string> = [];
|
|
2727
|
+
for (const [pattern, bodySays] of SUMMARY_CAUSES) {
|
|
2728
|
+
const match: RegExpMatchArray | null = cause.match(pattern);
|
|
2729
|
+
if (match) {
|
|
2730
|
+
known.push(bodySays(match));
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
expect({ name, cause, known: known.length }).toEqual({
|
|
2734
|
+
name,
|
|
2735
|
+
cause,
|
|
2736
|
+
known: 1,
|
|
2737
|
+
});
|
|
2738
|
+
expect({ name, cause, inBody: body.includes(known[0]!) }).toEqual({
|
|
2739
|
+
name,
|
|
2740
|
+
cause,
|
|
2741
|
+
inBody: true,
|
|
2742
|
+
});
|
|
2743
|
+
}
|
|
2744
|
+
},
|
|
2745
|
+
);
|
|
2746
|
+
|
|
2747
|
+
test("the summaries differ by system", () => {
|
|
2748
|
+
const summaries: Set<string> = new Set<string>(
|
|
2749
|
+
TROUBLESHOOTING_GUIDES.map(
|
|
2750
|
+
([, content]: [string, SetupGuideContent]): string => {
|
|
2751
|
+
return (
|
|
2752
|
+
findTopic(content.troubleshooting, "Broker health stays empty")
|
|
2753
|
+
.summary || ""
|
|
2754
|
+
);
|
|
2755
|
+
},
|
|
2756
|
+
),
|
|
2757
|
+
);
|
|
2758
|
+
/*
|
|
2759
|
+
* Kafka, RabbitMQ, Azure, CloudWatch, Pub/Sub, default, JMS, a system per
|
|
2760
|
+
* no-chart name, the long-tail system and the Azure queue without a namespace.
|
|
2761
|
+
*/
|
|
2762
|
+
expect(summaries.size).toBeGreaterThanOrEqual(10);
|
|
2763
|
+
});
|
|
2764
|
+
|
|
2765
|
+
test("an Azure queue without a namespace is pointed at the namespace rule", () => {
|
|
2766
|
+
const markdown: string = findTopic(
|
|
2767
|
+
queueContent({ system: "servicebus", destination: "orders" })
|
|
2768
|
+
.troubleshooting,
|
|
2769
|
+
"Broker health stays empty",
|
|
2770
|
+
).markdown;
|
|
2771
|
+
expect(markdown).toContain(
|
|
2772
|
+
"spans without a namespace host are on a different queue from Azure Monitor's metrics",
|
|
2773
|
+
);
|
|
2774
|
+
expect(docsEntry("Broker health stays empty")).toContain(
|
|
2775
|
+
"service bus and event hubs spans without a namespace host are on a different queue from azure monitor's metrics",
|
|
2776
|
+
);
|
|
2777
|
+
});
|
|
2778
|
+
});
|