@oneuptime/common 14.0.9 → 14.0.10
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/AutoRemediationSuggestion.ts +60 -0
- package/Models/DatabaseModels/CephCluster.ts +213 -0
- package/Models/DatabaseModels/DatabaseServer.ts +213 -0
- package/Models/DatabaseModels/DockerHost.ts +213 -0
- package/Models/DatabaseModels/DockerSwarmCluster.ts +213 -0
- package/Models/DatabaseModels/GlobalConfig.ts +1 -1
- package/Models/DatabaseModels/Host.ts +213 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/PodmanHost.ts +213 -0
- package/Models/DatabaseModels/ProxmoxCluster.ts +213 -0
- package/Models/DatabaseModels/ResourceAiAgent.ts +419 -0
- package/Models/DatabaseModels/RunnerJob.ts +144 -0
- package/Models/DatabaseModels/VMwareVCenter.ts +213 -0
- package/Server/API/AutoRemediationAPI.ts +392 -1
- package/Server/API/ResourceAiAccessAPI.ts +1669 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.ts +423 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Infrastructure/Semaphore.ts +22 -0
- package/Server/Middleware/TelemetryIngest.ts +15 -5
- 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/MetricRecordingRuleService.ts +47 -0
- package/Server/Services/PodmanHostService.ts +109 -1
- 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/TelemetryUsageBillingService.ts +28 -0
- package/Server/Services/TraceRecordingRuleService.ts +33 -0
- package/Server/Services/VMwareVCenterService.ts +110 -1
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1434 -32
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +935 -21
- package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +25 -1
- package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +577 -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 +873 -0
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +72 -2
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +72 -5
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +72 -5
- 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/SessionReplay/SessionReplayBudgetMetrics.ts +920 -0
- package/Server/Utils/SessionReplay/SessionReplayUsage.ts +84 -6
- 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 +298 -224
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +35 -15
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +389 -175
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +366 -143
- 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/Dashboard/ClusterAccessNotice.test.tsx +93 -0
- package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +18 -6
- package/Tests/App/Dashboard/InvestigationInfrastructureTools.test.tsx +506 -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/ResourceAiAgentInstall.test.ts +860 -0
- package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +1721 -0
- package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +1002 -0
- package/Tests/App/Dashboard/ResourceAiInsightsPage.test.tsx +931 -0
- package/Tests/App/Dashboard/ResourceAiNavigation.test.tsx +575 -0
- package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +38 -7
- package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +33 -1
- package/Tests/Models/DatabaseModels/ResourceAiAccessColumns.test.ts +738 -0
- package/Tests/Models/DatabaseModels/ResourceAiAgentModel.test.ts +842 -0
- package/Tests/Server/API/AutoRemediationApproveResourceRound.test.ts +835 -0
- package/Tests/Server/API/ResourceAiAccessAPI.test.ts +2730 -0
- package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +60 -0
- package/Tests/Server/Infrastructure/Postgres/AddResourceAiAgentsMigration.test.ts +659 -0
- package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +31 -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/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/MonitorProbeServiceIntervalScheduling.test.ts +45 -6
- 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/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/TelemetryUsageBillingSloExclusion.test.ts +218 -0
- package/Tests/Server/TestingUtils/Services/FakeRunnerJobCount.ts +145 -0
- package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +453 -0
- package/Tests/Server/Utils/AI/InvestigationInfrastructureReport.test.ts +721 -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/RemediationExecutionRunnerResourceMode.test.ts +1249 -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 +722 -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 +830 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorResource.test.ts +717 -0
- package/Tests/Server/Utils/AutoRemediation/RemediationVerifierResourceFollowUp.test.ts +338 -0
- package/Tests/Server/Utils/Monitor/Criteria/SessionReplayBudgetTemplateCriteria.test.ts +422 -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/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/AutoRemediation/AiRemediationCommandPlan.test.ts +10 -4
- package/Tests/Types/AutoRemediation/AiRemediationCommandPlanResourceCommand.test.ts +305 -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 +517 -0
- package/Tests/Types/Runbook/RunbookStepType.test.ts +147 -8
- package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +769 -1
- 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/SessionReplay/SessionReplayBudgetMetricType.test.ts +383 -0
- package/Types/AI/ResourceAiAccessApi.ts +140 -0
- package/Types/AI/ResourceAiAccessPermissions.ts +126 -0
- package/Types/AutoRemediation/AiRemediationCommandPlan.ts +183 -2
- 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/ResourceAiAgent/AiResourceType.ts +310 -0
- package/Types/ResourceAiAgent/ResourceAiAccess.ts +574 -0
- package/Types/Rum/SessionReplayBudgetMetricType.ts +47 -0
- package/Types/Runbook/RunbookStepType.ts +29 -0
- package/Types/Telemetry/TelemetryIngestSurface.ts +14 -2
- 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/SessionReplay/SessionReplayBudgetMetricType.ts +184 -0
- 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 +219 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseServer.js +219 -0
- package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +219 -0
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +219 -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/Host.js +219 -0
- package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/PodmanHost.js +219 -0
- package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +219 -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/RunnerJob.js +145 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js +219 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +231 -1
- package/build/dist/Server/API/AutoRemediationAPI.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/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/Index.js +2 -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/TelemetryIngest.js +12 -5
- package/build/dist/Server/Middleware/TelemetryIngest.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/MetricRecordingRuleService.js +46 -0
- package/build/dist/Server/Services/MetricRecordingRuleService.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/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/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/VMwareVCenterService.js +104 -0
- package/build/dist/Server/Services/VMwareVCenterService.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1029 -30
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +633 -28
- 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 +330 -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 +600 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +50 -4
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +55 -4
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +55 -4
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.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/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/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 +183 -209
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +19 -15
- 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 +263 -110
- 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/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/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/ResourceAiAgent/AiResourceType.js +242 -0
- package/build/dist/Types/ResourceAiAgent/AiResourceType.js.map +1 -0
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +275 -0
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -0
- 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/Telemetry/TelemetryIngestSurface.js +13 -2
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.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/SessionReplay/SessionReplayBudgetMetricType.js +160 -0
- package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js.map +1 -0
- package/package.json +1 -1
|
@@ -8,9 +8,17 @@ import {
|
|
|
8
8
|
} from "@testing-library/react";
|
|
9
9
|
import * as React from "react";
|
|
10
10
|
import { MemoryRouter } from "react-router-dom";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
afterEach,
|
|
13
|
+
beforeEach,
|
|
14
|
+
describe,
|
|
15
|
+
expect,
|
|
16
|
+
it,
|
|
17
|
+
jest,
|
|
18
|
+
} from "@jest/globals";
|
|
12
19
|
import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
|
|
13
20
|
import HTTPResponse from "../../../Types/API/HTTPResponse";
|
|
21
|
+
import Navigation from "../../../UI/Utils/Navigation";
|
|
14
22
|
import { JSONObject } from "../../../Types/JSON";
|
|
15
23
|
import getJestMockFunction, { MockFunction } from "../../MockType";
|
|
16
24
|
import { RecordingHealthStatus } from "../../../Types/Rum/SessionReplayHealth";
|
|
@@ -25,9 +33,15 @@ import { diagnoseRecordingHealth } from "../../../Utils/Rum/SessionReplayHealth"
|
|
|
25
33
|
* only when a ceiling exists; the policy and recorder panels never leak a
|
|
26
34
|
* raw enum or a blank; the paste box sits in its own panel; and a counter the
|
|
27
35
|
* server could not read says "unknown", never 0.
|
|
36
|
+
*
|
|
37
|
+
* The budget panel's "Set up alerts" link is followed through to the
|
|
38
|
+
* Recommendations page it opens, so the link and the cards it promises are
|
|
39
|
+
* checked against each other rather than each against a copy of the other.
|
|
28
40
|
*/
|
|
29
41
|
|
|
30
42
|
const postMock: MockFunction = getJestMockFunction();
|
|
43
|
+
const getItemMock: MockFunction = getJestMockFunction();
|
|
44
|
+
const getListMock: MockFunction = getJestMockFunction();
|
|
31
45
|
|
|
32
46
|
jest.mock("../../../UI/Utils/API/API", () => {
|
|
33
47
|
return {
|
|
@@ -52,6 +66,28 @@ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
|
|
|
52
66
|
getCommonHeaders: (): Record<string, string> => {
|
|
53
67
|
return {};
|
|
54
68
|
},
|
|
69
|
+
/*
|
|
70
|
+
* Read only by the Recommendations page "Set up alerts" opens (see
|
|
71
|
+
* "the page Set up alerts opens"); the Health page itself never lists.
|
|
72
|
+
*/
|
|
73
|
+
getItem: (...args: Array<unknown>) => {
|
|
74
|
+
return getItemMock(...args);
|
|
75
|
+
},
|
|
76
|
+
getList: (...args: Array<unknown>) => {
|
|
77
|
+
return getListMock(...args);
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// The Recommendations page's owner picker; nobody to offer here.
|
|
84
|
+
jest.mock("../../../../App/FeatureSet/Dashboard/src/Utils/ProjectUser", () => {
|
|
85
|
+
return {
|
|
86
|
+
__esModule: true,
|
|
87
|
+
default: {
|
|
88
|
+
fetchProjectUsersAsDropdownOptions: () => {
|
|
89
|
+
return Promise.resolve([]);
|
|
90
|
+
},
|
|
55
91
|
},
|
|
56
92
|
};
|
|
57
93
|
});
|
|
@@ -65,6 +101,35 @@ import {
|
|
|
65
101
|
SessionReplayHealthSnapshot,
|
|
66
102
|
clearSessionReplayHealthStore,
|
|
67
103
|
} from "../../../../App/FeatureSet/Dashboard/src/Components/SessionReplay/useSessionReplayHealth";
|
|
104
|
+
import {
|
|
105
|
+
getBudgetAlertRecommendationsRoute,
|
|
106
|
+
getReplayPolicyPageRoute,
|
|
107
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Components/SessionReplay/RecordingHealthCard";
|
|
108
|
+
import RumApplicationRecommendations from "../../../../App/FeatureSet/Dashboard/src/Pages/Rum/View/Recommendations";
|
|
109
|
+
import RecommendationFilterUtil from "../../../../App/FeatureSet/Dashboard/src/Components/Recommendations/RecommendationFilterUtil";
|
|
110
|
+
import RecommendationResourceRegistry from "../../../../App/FeatureSet/Dashboard/src/Components/Recommendations/RecommendationResourceRegistry";
|
|
111
|
+
import {
|
|
112
|
+
RecommendationSeverityFilter,
|
|
113
|
+
RecommendationStatusFilter,
|
|
114
|
+
RecommendationViewModel,
|
|
115
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Components/Recommendations/RecommendationViewModel";
|
|
116
|
+
import RouteMap from "../../../../App/FeatureSet/Dashboard/src/Utils/RouteMap";
|
|
117
|
+
import PageMap from "../../../../App/FeatureSet/Dashboard/src/Utils/PageMap";
|
|
118
|
+
import Route from "../../../Types/API/Route";
|
|
119
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
120
|
+
import RumApplication from "../../../Models/DatabaseModels/RumApplication";
|
|
121
|
+
import MonitorStatus from "../../../Models/DatabaseModels/MonitorStatus";
|
|
122
|
+
import IncidentSeverity from "../../../Models/DatabaseModels/IncidentSeverity";
|
|
123
|
+
import AlertSeverity from "../../../Models/DatabaseModels/AlertSeverity";
|
|
124
|
+
import MonitorRecommendationCatalog from "../../../Types/Monitor/Recommendation/MonitorRecommendationCatalog";
|
|
125
|
+
import MonitorRecommendationUtil from "../../../Types/Monitor/Recommendation/MonitorRecommendationUtil";
|
|
126
|
+
import Monitor from "../../../Models/DatabaseModels/Monitor";
|
|
127
|
+
import MonitorType from "../../../Types/Monitor/MonitorType";
|
|
128
|
+
import {
|
|
129
|
+
MonitorRecommendation,
|
|
130
|
+
MonitorRecommendationContext,
|
|
131
|
+
MonitorRecommendationResourceType,
|
|
132
|
+
} from "../../../Types/Monitor/Recommendation/MonitorRecommendationTypes";
|
|
68
133
|
|
|
69
134
|
const APP_ID: string = "0193c0de-1111-4aaa-8bbb-000000000001";
|
|
70
135
|
const NOW: number = Date.parse("2026-09-05T10:00:00.000Z");
|
|
@@ -716,6 +781,709 @@ describe("RecordingHealthDashboardView storage panel", () => {
|
|
|
716
781
|
});
|
|
717
782
|
});
|
|
718
783
|
|
|
784
|
+
/*
|
|
785
|
+
* "Set up alerts": the Storage budget panel's link to the session replay
|
|
786
|
+
* storage budget alerts on this application's Recommendations page. It is
|
|
787
|
+
* drawn on the fact that makes that page offer them - replay on for this
|
|
788
|
+
* application, and at least one replay recorded - so it never opens on a
|
|
789
|
+
* search that matches no card.
|
|
790
|
+
*/
|
|
791
|
+
describe("RecordingHealthDashboardView storage panel: Set up alerts", () => {
|
|
792
|
+
function budgetAlertsAnchor(): HTMLElement {
|
|
793
|
+
const anchor: HTMLElement | null = screen
|
|
794
|
+
.getByTestId("health-budget-alerts")
|
|
795
|
+
.closest("a");
|
|
796
|
+
|
|
797
|
+
expect(anchor).not.toBeNull();
|
|
798
|
+
|
|
799
|
+
return anchor as HTMLElement;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
it("an application that records gets the link, to its Recommendations page opened on ?search=budget&status=All", () => {
|
|
803
|
+
renderView(makeSnapshot(makeStatus()));
|
|
804
|
+
|
|
805
|
+
expect(screen.getByTestId("health-budget-alerts")).toHaveTextContent(
|
|
806
|
+
"Set up alerts",
|
|
807
|
+
);
|
|
808
|
+
|
|
809
|
+
const href: string = budgetAlertsAnchor().getAttribute("href") ?? "";
|
|
810
|
+
|
|
811
|
+
expect(href.endsWith("/recommendations?search=budget&status=All")).toBe(
|
|
812
|
+
true,
|
|
813
|
+
);
|
|
814
|
+
expect(href).toContain(
|
|
815
|
+
`/rum/${APP_ID}/recommendations?search=budget&status=All`,
|
|
816
|
+
);
|
|
817
|
+
|
|
818
|
+
// A page of the product, opened in place like the link beside it.
|
|
819
|
+
expect(budgetAlertsAnchor()).not.toHaveAttribute("target");
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
it("sits in the Storage budget header, stacked under Change budget", () => {
|
|
823
|
+
renderView(makeSnapshot(makeStatus()));
|
|
824
|
+
|
|
825
|
+
const panel: HTMLElement = screen.getByTestId("health-bytes");
|
|
826
|
+
const changeBudget: HTMLElement = within(panel).getByTestId(
|
|
827
|
+
"health-change-budget",
|
|
828
|
+
);
|
|
829
|
+
const setUpAlerts: HTMLElement = within(panel).getByTestId(
|
|
830
|
+
"health-budget-alerts",
|
|
831
|
+
);
|
|
832
|
+
const stack: HTMLElement | null = changeBudget.closest("div.flex-col");
|
|
833
|
+
|
|
834
|
+
expect(stack).not.toBeNull();
|
|
835
|
+
expect(stack).toHaveClass("items-end", "gap-1");
|
|
836
|
+
expect(stack).toContainElement(setUpAlerts);
|
|
837
|
+
expect((stack as HTMLElement).closest("header")).not.toBeNull();
|
|
838
|
+
|
|
839
|
+
// Change budget first, Set up alerts under it.
|
|
840
|
+
expect(
|
|
841
|
+
changeBudget.compareDocumentPosition(setUpAlerts) &
|
|
842
|
+
Node.DOCUMENT_POSITION_FOLLOWING,
|
|
843
|
+
).toBeTruthy();
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
/*
|
|
847
|
+
* The Recommendations page is tall; opened at the Health page's scroll
|
|
848
|
+
* position it lands past its search box and status tiles, which are what
|
|
849
|
+
* the link sets up. The side menu starts every page at the top the same way.
|
|
850
|
+
*/
|
|
851
|
+
it("opens its page at the top, like a side menu link, not at the panel's scroll position", () => {
|
|
852
|
+
renderView(makeSnapshot(makeStatus()));
|
|
853
|
+
|
|
854
|
+
const scrollTo: ReturnType<typeof jest.spyOn> = jest
|
|
855
|
+
.spyOn(window, "scrollTo")
|
|
856
|
+
.mockImplementation((): void => {});
|
|
857
|
+
const navigate: ReturnType<typeof jest.spyOn> = jest
|
|
858
|
+
.spyOn(Navigation, "navigate")
|
|
859
|
+
.mockImplementation((): void => {});
|
|
860
|
+
|
|
861
|
+
try {
|
|
862
|
+
fireEvent.click(budgetAlertsAnchor());
|
|
863
|
+
|
|
864
|
+
expect(scrollTo).toHaveBeenCalledWith({ top: 0, behavior: "smooth" });
|
|
865
|
+
expect(navigate).toHaveBeenCalledTimes(1);
|
|
866
|
+
expect(String(navigate.mock.calls[0]![0])).toContain(
|
|
867
|
+
`/rum/${APP_ID}/recommendations?search=budget&status=All`,
|
|
868
|
+
);
|
|
869
|
+
} finally {
|
|
870
|
+
scrollTo.mockRestore();
|
|
871
|
+
navigate.mockRestore();
|
|
872
|
+
}
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
it("an application that has never recorded gets no link, and keeps Change budget", () => {
|
|
876
|
+
renderView(
|
|
877
|
+
makeSnapshot(
|
|
878
|
+
makeStatus({ lastChunkReceivedAt: null, monthlyBudgetInGB: 5 }),
|
|
879
|
+
),
|
|
880
|
+
);
|
|
881
|
+
|
|
882
|
+
expect(screen.getByTestId("health-bytes")).toBeInTheDocument();
|
|
883
|
+
expect(screen.queryByTestId("health-budget-alerts")).toBeNull();
|
|
884
|
+
expect(screen.getByTestId("health-change-budget")).toBeInTheDocument();
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
it("an application with replay switched off gets no link, even with past recordings and a budget", () => {
|
|
888
|
+
renderView(
|
|
889
|
+
makeSnapshot({
|
|
890
|
+
...withPolicy({ isApplicationEnabled: false }),
|
|
891
|
+
monthlyBudgetInGB: 5,
|
|
892
|
+
}),
|
|
893
|
+
);
|
|
894
|
+
|
|
895
|
+
expect(level()).toBe("disabled-app");
|
|
896
|
+
expect(screen.queryByTestId("health-budget-alerts")).toBeNull();
|
|
897
|
+
expect(screen.getByTestId("health-change-budget")).toBeInTheDocument();
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
it("the project switch does not decide it: like the Recommendations page, the link reads the application's own", () => {
|
|
901
|
+
renderView(makeSnapshot(withPolicy({ isProjectEnabled: false })));
|
|
902
|
+
|
|
903
|
+
expect(level()).toBe("disabled-project");
|
|
904
|
+
expect(screen.getByTestId("health-budget-alerts")).toBeInTheDocument();
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
it("no monthly budget keeps the link: the daily limit's alerts are offered on their own", () => {
|
|
908
|
+
renderView(
|
|
909
|
+
makeSnapshot(
|
|
910
|
+
makeStatus({
|
|
911
|
+
applicationBytesUsedThisMonth: 3 * MB,
|
|
912
|
+
monthlyBudgetInGB: null,
|
|
913
|
+
}),
|
|
914
|
+
),
|
|
915
|
+
);
|
|
916
|
+
|
|
917
|
+
expect(screen.getByTestId("health-meter-app-month")).toHaveAttribute(
|
|
918
|
+
"data-kind",
|
|
919
|
+
"unlimited",
|
|
920
|
+
);
|
|
921
|
+
expect(screen.getByTestId("health-budget-alerts")).toBeInTheDocument();
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
it("a spent budget keeps the link: the moment someone most wants the alert next time", () => {
|
|
925
|
+
renderView(
|
|
926
|
+
makeSnapshot(
|
|
927
|
+
makeStatus({ projectBytesUsedToday: GB, dailyByteLimit: GB }),
|
|
928
|
+
),
|
|
929
|
+
);
|
|
930
|
+
|
|
931
|
+
expect(level()).toBe("budget-paused");
|
|
932
|
+
expect(screen.getByTestId("health-budget-alerts")).toBeInTheDocument();
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
it("without a status there is no panel, so no link", () => {
|
|
936
|
+
const { unmount } = renderView(
|
|
937
|
+
makeSnapshot(null, { isLoading: true, fetchedAtUnixMs: null }),
|
|
938
|
+
);
|
|
939
|
+
|
|
940
|
+
expect(screen.queryByTestId("health-budget-alerts")).toBeNull();
|
|
941
|
+
|
|
942
|
+
unmount();
|
|
943
|
+
renderView(
|
|
944
|
+
makeSnapshot(null, {
|
|
945
|
+
error: { kind: "permission", message: "Forbidden" },
|
|
946
|
+
fetchedAtUnixMs: null,
|
|
947
|
+
}),
|
|
948
|
+
);
|
|
949
|
+
|
|
950
|
+
expect(screen.queryByTestId("health-bytes")).toBeNull();
|
|
951
|
+
expect(screen.queryByTestId("health-budget-alerts")).toBeNull();
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
it("building the link leaves the shared route table alone, however often it is built", () => {
|
|
955
|
+
const shared: string = (
|
|
956
|
+
RouteMap[PageMap.RUM_APPLICATION_VIEW_RECOMMENDATIONS] as Route
|
|
957
|
+
).toString();
|
|
958
|
+
|
|
959
|
+
const first: string = getBudgetAlertRecommendationsRoute(APP_ID).toString();
|
|
960
|
+
const second: string = getBudgetAlertRecommendationsRoute(
|
|
961
|
+
new ObjectID(APP_ID),
|
|
962
|
+
).toString();
|
|
963
|
+
|
|
964
|
+
// A string id and an ObjectID build the same single-query link.
|
|
965
|
+
expect(second).toBe(first);
|
|
966
|
+
expect(first.split("?")).toHaveLength(2);
|
|
967
|
+
expect(
|
|
968
|
+
first.endsWith(`/rum/${APP_ID}/recommendations?search=budget&status=All`),
|
|
969
|
+
).toBe(true);
|
|
970
|
+
|
|
971
|
+
/*
|
|
972
|
+
* addQueryParams appends in place: chained on the shared entry instead
|
|
973
|
+
* of populateRouteParams' copy, every later link to the page - the side
|
|
974
|
+
* menu's included - would carry the search, once more per render.
|
|
975
|
+
*/
|
|
976
|
+
expect(
|
|
977
|
+
(
|
|
978
|
+
RouteMap[PageMap.RUM_APPLICATION_VIEW_RECOMMENDATIONS] as Route
|
|
979
|
+
).toString(),
|
|
980
|
+
).toBe(shared);
|
|
981
|
+
expect(shared).not.toContain("?");
|
|
982
|
+
expect(getReplayPolicyPageRoute(APP_ID).toString()).not.toContain("?");
|
|
983
|
+
});
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
/*
|
|
987
|
+
* Where "Set up alerts" lands, worked out with the Recommendations page's own
|
|
988
|
+
* machinery: the RUM row of RecommendationResourceRegistry reads the context
|
|
989
|
+
* from the application's columns, the catalog answers with the RUM alert
|
|
990
|
+
* templates for that context, and RecommendationFilterUtil applies the search
|
|
991
|
+
* the link carries.
|
|
992
|
+
*/
|
|
993
|
+
describe("where Set up alerts lands", () => {
|
|
994
|
+
const DAILY_BUDGET_TEMPLATE_IDS: Array<string> = [
|
|
995
|
+
"rum-session-replay-daily-budget-nearly-spent",
|
|
996
|
+
"rum-session-replay-daily-budget-spent",
|
|
997
|
+
];
|
|
998
|
+
const ALL_BUDGET_TEMPLATE_IDS: Array<string> = [
|
|
999
|
+
...DAILY_BUDGET_TEMPLATE_IDS,
|
|
1000
|
+
"rum-session-replay-monthly-budget-nearly-spent",
|
|
1001
|
+
"rum-session-replay-monthly-budget-spent",
|
|
1002
|
+
];
|
|
1003
|
+
|
|
1004
|
+
// The search the link carries, read off the route the link is built from.
|
|
1005
|
+
const LINK_SEARCH_TEXT: string =
|
|
1006
|
+
new URLSearchParams(
|
|
1007
|
+
getBudgetAlertRecommendationsRoute(APP_ID).toString().split("?")[1],
|
|
1008
|
+
).get("search") ?? "";
|
|
1009
|
+
|
|
1010
|
+
/*
|
|
1011
|
+
* The RumApplication row behind a status: /ingest-status (TelemetryAPI)
|
|
1012
|
+
* reads isApplicationEnabled, lastChunkReceivedAt and monthlyBudgetInGB
|
|
1013
|
+
* off exactly these three columns.
|
|
1014
|
+
*/
|
|
1015
|
+
function rumApplicationRow(status: RecordingHealthStatus): RumApplication {
|
|
1016
|
+
const row: RumApplication = new RumApplication();
|
|
1017
|
+
|
|
1018
|
+
row._id = APP_ID;
|
|
1019
|
+
row.name = status.appIdentifier;
|
|
1020
|
+
row.isSessionReplayEnabled = status.policy.isApplicationEnabled;
|
|
1021
|
+
|
|
1022
|
+
if (status.lastChunkReceivedAt !== null) {
|
|
1023
|
+
row.sessionReplayLastChunkReceivedAt = new Date(
|
|
1024
|
+
status.lastChunkReceivedAt,
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
if (status.monthlyBudgetInGB !== null) {
|
|
1029
|
+
row.sessionReplayMonthlyBudgetInGB = status.monthlyBudgetInGB;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
return row;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
function listedTemplateIds(
|
|
1036
|
+
recommendations: Array<MonitorRecommendation>,
|
|
1037
|
+
searchText: string,
|
|
1038
|
+
): Array<string> {
|
|
1039
|
+
return RecommendationFilterUtil.filter({
|
|
1040
|
+
viewModels: RecommendationFilterUtil.buildViewModels({
|
|
1041
|
+
recommendations: recommendations,
|
|
1042
|
+
coveredMonitorIds: new Map<string, ObjectID>(),
|
|
1043
|
+
dismissals: [],
|
|
1044
|
+
}),
|
|
1045
|
+
filterState: {
|
|
1046
|
+
searchText: searchText,
|
|
1047
|
+
status: RecommendationStatusFilter.All,
|
|
1048
|
+
severity: RecommendationSeverityFilter.All,
|
|
1049
|
+
},
|
|
1050
|
+
})
|
|
1051
|
+
.map((viewModel: RecommendationViewModel): string => {
|
|
1052
|
+
return viewModel.recommendation.templateId;
|
|
1053
|
+
})
|
|
1054
|
+
.sort();
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
it("the link's search is the one word that picks out the budget alerts", () => {
|
|
1058
|
+
renderView(makeSnapshot(makeStatus()));
|
|
1059
|
+
|
|
1060
|
+
const href: string =
|
|
1061
|
+
screen
|
|
1062
|
+
.getByTestId("health-budget-alerts")
|
|
1063
|
+
.closest("a")
|
|
1064
|
+
?.getAttribute("href") ?? "";
|
|
1065
|
+
|
|
1066
|
+
expect(LINK_SEARCH_TEXT).toBe("budget");
|
|
1067
|
+
expect(new URLSearchParams(href.split("?")[1]).get("search")).toBe(
|
|
1068
|
+
LINK_SEARCH_TEXT,
|
|
1069
|
+
);
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
it("across everything the RUM catalog can offer, that search matches the four storage budget alerts and nothing else", () => {
|
|
1073
|
+
expect(
|
|
1074
|
+
listedTemplateIds(
|
|
1075
|
+
MonitorRecommendationCatalog.getAllPossibleRecommendations(
|
|
1076
|
+
MonitorRecommendationResourceType.RumApplication,
|
|
1077
|
+
),
|
|
1078
|
+
LINK_SEARCH_TEXT,
|
|
1079
|
+
),
|
|
1080
|
+
).toEqual([...ALL_BUDGET_TEMPLATE_IDS].sort());
|
|
1081
|
+
});
|
|
1082
|
+
|
|
1083
|
+
type LandingCase = [string, RecordingHealthStatus, Array<string>];
|
|
1084
|
+
|
|
1085
|
+
const LANDING_CASES: Array<LandingCase> = [
|
|
1086
|
+
[
|
|
1087
|
+
"recorded, no monthly budget: the link, over the daily pair",
|
|
1088
|
+
makeStatus(),
|
|
1089
|
+
DAILY_BUDGET_TEMPLATE_IDS,
|
|
1090
|
+
],
|
|
1091
|
+
[
|
|
1092
|
+
"recorded, with a monthly budget: the link, over all four",
|
|
1093
|
+
makeStatus({ monthlyBudgetInGB: 5 }),
|
|
1094
|
+
ALL_BUDGET_TEMPLATE_IDS,
|
|
1095
|
+
],
|
|
1096
|
+
[
|
|
1097
|
+
"recorded, a budget of 0 (no ceiling): the link, over the daily pair",
|
|
1098
|
+
makeStatus({ monthlyBudgetInGB: 0 }),
|
|
1099
|
+
DAILY_BUDGET_TEMPLATE_IDS,
|
|
1100
|
+
],
|
|
1101
|
+
[
|
|
1102
|
+
"never recorded, with a monthly budget: no link, and nothing to land on",
|
|
1103
|
+
makeStatus({ lastChunkReceivedAt: null, monthlyBudgetInGB: 5 }),
|
|
1104
|
+
[],
|
|
1105
|
+
],
|
|
1106
|
+
[
|
|
1107
|
+
"switched off after recording, with a budget: no link, and nothing to land on",
|
|
1108
|
+
{
|
|
1109
|
+
...withPolicy({ isApplicationEnabled: false }),
|
|
1110
|
+
monthlyBudgetInGB: 5,
|
|
1111
|
+
},
|
|
1112
|
+
[],
|
|
1113
|
+
],
|
|
1114
|
+
[
|
|
1115
|
+
"switched off and never recorded: no link, and nothing to land on",
|
|
1116
|
+
{
|
|
1117
|
+
...withPolicy({ isApplicationEnabled: false }),
|
|
1118
|
+
lastChunkReceivedAt: null,
|
|
1119
|
+
},
|
|
1120
|
+
[],
|
|
1121
|
+
],
|
|
1122
|
+
[
|
|
1123
|
+
"project switched off, application on and recorded: the link, over the daily pair",
|
|
1124
|
+
withPolicy({ isProjectEnabled: false }),
|
|
1125
|
+
DAILY_BUDGET_TEMPLATE_IDS,
|
|
1126
|
+
],
|
|
1127
|
+
];
|
|
1128
|
+
|
|
1129
|
+
it.each(LANDING_CASES)(
|
|
1130
|
+
"%s",
|
|
1131
|
+
(_name: string, status: RecordingHealthStatus, expected: Array<string>) => {
|
|
1132
|
+
renderView(makeSnapshot(status));
|
|
1133
|
+
|
|
1134
|
+
const context: MonitorRecommendationContext =
|
|
1135
|
+
RecommendationResourceRegistry.readContext({
|
|
1136
|
+
resourceType: MonitorRecommendationResourceType.RumApplication,
|
|
1137
|
+
model: rumApplicationRow(status),
|
|
1138
|
+
});
|
|
1139
|
+
|
|
1140
|
+
const landedOn: Array<string> = listedTemplateIds(
|
|
1141
|
+
MonitorRecommendationCatalog.getRecommendations(
|
|
1142
|
+
MonitorRecommendationResourceType.RumApplication,
|
|
1143
|
+
context,
|
|
1144
|
+
),
|
|
1145
|
+
LINK_SEARCH_TEXT,
|
|
1146
|
+
);
|
|
1147
|
+
|
|
1148
|
+
expect(landedOn).toEqual([...expected].sort());
|
|
1149
|
+
|
|
1150
|
+
// The link is drawn exactly when there is a card for it to land on.
|
|
1151
|
+
expect(screen.queryByTestId("health-budget-alerts") !== null).toBe(
|
|
1152
|
+
landedOn.length > 0,
|
|
1153
|
+
);
|
|
1154
|
+
},
|
|
1155
|
+
);
|
|
1156
|
+
});
|
|
1157
|
+
|
|
1158
|
+
/*
|
|
1159
|
+
* The Recommendations page the link opens, rendered for real (the same
|
|
1160
|
+
* scaffolding as Tests/App/Dashboard/RecommendationsLoadedContext.test.tsx):
|
|
1161
|
+
* the search box starts on the link's word, only the storage budget alerts
|
|
1162
|
+
* are listed, and the word is only a starting point.
|
|
1163
|
+
*/
|
|
1164
|
+
describe("the page Set up alerts opens", () => {
|
|
1165
|
+
/*
|
|
1166
|
+
* A UUID, because ProjectUtil ignores anything else and the link would keep
|
|
1167
|
+
* its ":projectId" placeholder.
|
|
1168
|
+
*/
|
|
1169
|
+
const PROJECT_ID: string = "8f2a1b3c-4d5e-4f60-9a7b-1c2d3e4f5a6b";
|
|
1170
|
+
|
|
1171
|
+
// Everything MonitorRecommendations needs before it lists anything.
|
|
1172
|
+
function projectList(modelType: unknown): { data: Array<unknown> } {
|
|
1173
|
+
if (modelType === MonitorStatus) {
|
|
1174
|
+
const online: MonitorStatus = new MonitorStatus();
|
|
1175
|
+
online._id = ObjectID.generate().toString();
|
|
1176
|
+
online.isOperationalState = true;
|
|
1177
|
+
|
|
1178
|
+
const offline: MonitorStatus = new MonitorStatus();
|
|
1179
|
+
offline._id = ObjectID.generate().toString();
|
|
1180
|
+
offline.isOfflineState = true;
|
|
1181
|
+
|
|
1182
|
+
return { data: [online, offline] };
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
if (modelType === IncidentSeverity || modelType === AlertSeverity) {
|
|
1186
|
+
const severity: IncidentSeverity = new IncidentSeverity();
|
|
1187
|
+
severity._id = ObjectID.generate().toString();
|
|
1188
|
+
severity.name = "Critical";
|
|
1189
|
+
|
|
1190
|
+
return { data: [severity] };
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// No monitors yet, no dismissals, no policies, teams or labels.
|
|
1194
|
+
return { data: [] };
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
function rumApplicationRow(monthlyBudgetInGB: number | null): RumApplication {
|
|
1198
|
+
const row: RumApplication = new RumApplication();
|
|
1199
|
+
|
|
1200
|
+
row._id = APP_ID;
|
|
1201
|
+
row.name = "acme-web";
|
|
1202
|
+
row.isSessionReplayEnabled = true;
|
|
1203
|
+
row.sessionReplayLastChunkReceivedAt = new Date(NOW - 12 * 1000);
|
|
1204
|
+
|
|
1205
|
+
if (monthlyBudgetInGB !== null) {
|
|
1206
|
+
row.sessionReplayMonthlyBudgetInGB = monthlyBudgetInGB;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
return row;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
// The cards on screen, by template id.
|
|
1213
|
+
function listedTemplateIds(): Array<string> {
|
|
1214
|
+
const templateIdByRecommendationId: Map<string, string> = new Map<
|
|
1215
|
+
string,
|
|
1216
|
+
string
|
|
1217
|
+
>(
|
|
1218
|
+
MonitorRecommendationCatalog.getAllPossibleRecommendations(
|
|
1219
|
+
MonitorRecommendationResourceType.RumApplication,
|
|
1220
|
+
).map((recommendation: MonitorRecommendation): [string, string] => {
|
|
1221
|
+
return [recommendation.recommendationId, recommendation.templateId];
|
|
1222
|
+
}),
|
|
1223
|
+
);
|
|
1224
|
+
|
|
1225
|
+
return Array.from(
|
|
1226
|
+
document.querySelectorAll<HTMLElement>(
|
|
1227
|
+
'[data-testid^="recommendation-card-"]',
|
|
1228
|
+
),
|
|
1229
|
+
)
|
|
1230
|
+
.map((card: HTMLElement): string => {
|
|
1231
|
+
return (card.getAttribute("data-testid") ?? "").replace(
|
|
1232
|
+
"recommendation-card-",
|
|
1233
|
+
"",
|
|
1234
|
+
);
|
|
1235
|
+
})
|
|
1236
|
+
.filter((recommendationId: string): boolean => {
|
|
1237
|
+
// Drops the cards' own inner test ids ("select-…", …).
|
|
1238
|
+
return templateIdByRecommendationId.has(recommendationId);
|
|
1239
|
+
})
|
|
1240
|
+
.map((recommendationId: string): string => {
|
|
1241
|
+
return templateIdByRecommendationId.get(recommendationId) as string;
|
|
1242
|
+
})
|
|
1243
|
+
.sort();
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
// The Recommendations page, rendered at `href` for the application.
|
|
1247
|
+
async function openRecommendations(
|
|
1248
|
+
href: string,
|
|
1249
|
+
monthlyBudgetInGB: number | null,
|
|
1250
|
+
): Promise<void> {
|
|
1251
|
+
window.history.pushState({}, "", href);
|
|
1252
|
+
getItemMock.mockResolvedValue(rumApplicationRow(monthlyBudgetInGB));
|
|
1253
|
+
|
|
1254
|
+
render(
|
|
1255
|
+
<MemoryRouter>
|
|
1256
|
+
<RumApplicationRecommendations
|
|
1257
|
+
pageRoute={new Route(href.split("?")[0])}
|
|
1258
|
+
currentProject={null}
|
|
1259
|
+
hasPaymentMethod={true}
|
|
1260
|
+
/>
|
|
1261
|
+
</MemoryRouter>,
|
|
1262
|
+
);
|
|
1263
|
+
|
|
1264
|
+
await screen.findByTestId("recommendation-search");
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
// Health page first, as the link is built there; then the link, followed.
|
|
1268
|
+
async function followSetUpAlerts(
|
|
1269
|
+
monthlyBudgetInGB: number | null,
|
|
1270
|
+
): Promise<void> {
|
|
1271
|
+
window.history.pushState(
|
|
1272
|
+
{},
|
|
1273
|
+
"",
|
|
1274
|
+
`/dashboard/${PROJECT_ID}/rum/${APP_ID}/session-replay-health`,
|
|
1275
|
+
);
|
|
1276
|
+
|
|
1277
|
+
const { unmount } = renderView(
|
|
1278
|
+
makeSnapshot(makeStatus({ monthlyBudgetInGB: monthlyBudgetInGB })),
|
|
1279
|
+
);
|
|
1280
|
+
|
|
1281
|
+
const href: string =
|
|
1282
|
+
screen
|
|
1283
|
+
.getByTestId("health-budget-alerts")
|
|
1284
|
+
.closest("a")
|
|
1285
|
+
?.getAttribute("href") ?? "";
|
|
1286
|
+
|
|
1287
|
+
unmount();
|
|
1288
|
+
|
|
1289
|
+
expect(href).toBe(
|
|
1290
|
+
`/dashboard/${PROJECT_ID}/rum/${APP_ID}/recommendations?search=budget&status=All`,
|
|
1291
|
+
);
|
|
1292
|
+
|
|
1293
|
+
await openRecommendations(href, monthlyBudgetInGB);
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
/*
|
|
1297
|
+
* Monitors already made from the daily budget pair for this application,
|
|
1298
|
+
* with the steps the page builds for them - which is what its coverage
|
|
1299
|
+
* check fingerprints.
|
|
1300
|
+
*/
|
|
1301
|
+
function createdDailyBudgetMonitors(): Array<Monitor> {
|
|
1302
|
+
return MonitorRecommendationCatalog.getAllPossibleRecommendations(
|
|
1303
|
+
MonitorRecommendationResourceType.RumApplication,
|
|
1304
|
+
)
|
|
1305
|
+
.filter((recommendation: MonitorRecommendation): boolean => {
|
|
1306
|
+
return recommendation.templateId.startsWith(
|
|
1307
|
+
"rum-session-replay-daily-budget-",
|
|
1308
|
+
);
|
|
1309
|
+
})
|
|
1310
|
+
.map((recommendation: MonitorRecommendation): Monitor => {
|
|
1311
|
+
const monitor: Monitor = new Monitor();
|
|
1312
|
+
monitor._id = ObjectID.generate().toString();
|
|
1313
|
+
monitor.name = `acme-web - ${recommendation.name}`;
|
|
1314
|
+
monitor.monitorType = MonitorType.Metrics;
|
|
1315
|
+
monitor.monitorSteps = MonitorRecommendationUtil.buildMonitorSteps({
|
|
1316
|
+
recommendation: recommendation,
|
|
1317
|
+
args: {
|
|
1318
|
+
resourceIdentifier: APP_ID,
|
|
1319
|
+
onlineMonitorStatusId: ObjectID.generate(),
|
|
1320
|
+
offlineMonitorStatusId: ObjectID.generate(),
|
|
1321
|
+
defaultIncidentSeverityId: ObjectID.generate(),
|
|
1322
|
+
defaultAlertSeverityId: ObjectID.generate(),
|
|
1323
|
+
monitorName: "acme-web",
|
|
1324
|
+
},
|
|
1325
|
+
defaultMonitorStatusId: ObjectID.generate(),
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
return monitor;
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
beforeEach(() => {
|
|
1333
|
+
getItemMock.mockReset();
|
|
1334
|
+
getListMock.mockReset();
|
|
1335
|
+
getListMock.mockImplementation((args: unknown) => {
|
|
1336
|
+
return Promise.resolve(
|
|
1337
|
+
projectList((args as { modelType: unknown }).modelType),
|
|
1338
|
+
);
|
|
1339
|
+
});
|
|
1340
|
+
});
|
|
1341
|
+
|
|
1342
|
+
afterEach(() => {
|
|
1343
|
+
window.history.pushState({}, "", "/");
|
|
1344
|
+
});
|
|
1345
|
+
|
|
1346
|
+
it("opens on 'budget' in the search box, listing all four budget alerts for an application with a monthly budget", async () => {
|
|
1347
|
+
await followSetUpAlerts(5);
|
|
1348
|
+
|
|
1349
|
+
expect(screen.getByTestId("recommendation-search")).toHaveValue("budget");
|
|
1350
|
+
|
|
1351
|
+
await waitFor(() => {
|
|
1352
|
+
expect(listedTemplateIds()).toEqual(
|
|
1353
|
+
[
|
|
1354
|
+
"rum-session-replay-daily-budget-nearly-spent",
|
|
1355
|
+
"rum-session-replay-daily-budget-spent",
|
|
1356
|
+
"rum-session-replay-monthly-budget-nearly-spent",
|
|
1357
|
+
"rum-session-replay-monthly-budget-spent",
|
|
1358
|
+
].sort(),
|
|
1359
|
+
);
|
|
1360
|
+
});
|
|
1361
|
+
|
|
1362
|
+
expect(screen.queryByText("Poor Largest Contentful Paint")).toBeNull();
|
|
1363
|
+
|
|
1364
|
+
// The row it read is the application the Health page was showing.
|
|
1365
|
+
expect(getItemMock).toHaveBeenCalledTimes(1);
|
|
1366
|
+
expect(
|
|
1367
|
+
(getItemMock.mock.calls[0]![0] as { id: ObjectID }).id.toString(),
|
|
1368
|
+
).toBe(APP_ID);
|
|
1369
|
+
});
|
|
1370
|
+
|
|
1371
|
+
it("an application without a monthly budget lands on the daily pair", async () => {
|
|
1372
|
+
await followSetUpAlerts(null);
|
|
1373
|
+
|
|
1374
|
+
expect(screen.getByTestId("recommendation-search")).toHaveValue("budget");
|
|
1375
|
+
|
|
1376
|
+
await waitFor(() => {
|
|
1377
|
+
expect(listedTemplateIds()).toEqual([
|
|
1378
|
+
"rum-session-replay-daily-budget-nearly-spent",
|
|
1379
|
+
"rum-session-replay-daily-budget-spent",
|
|
1380
|
+
]);
|
|
1381
|
+
});
|
|
1382
|
+
});
|
|
1383
|
+
|
|
1384
|
+
it("opens on every status, not only the alerts still to set up", async () => {
|
|
1385
|
+
await followSetUpAlerts(null);
|
|
1386
|
+
|
|
1387
|
+
expect(screen.getByTestId("recommendation-stat-All")).toHaveAttribute(
|
|
1388
|
+
"aria-pressed",
|
|
1389
|
+
"true",
|
|
1390
|
+
);
|
|
1391
|
+
expect(screen.getByTestId("recommendation-stat-Available")).toHaveAttribute(
|
|
1392
|
+
"aria-pressed",
|
|
1393
|
+
"false",
|
|
1394
|
+
);
|
|
1395
|
+
});
|
|
1396
|
+
|
|
1397
|
+
/*
|
|
1398
|
+
* The page opens on the alerts still to set up; the link opens it on all
|
|
1399
|
+
* of them. Otherwise the second time someone follows it - the alerts
|
|
1400
|
+
* already made - it would land on an empty list.
|
|
1401
|
+
*/
|
|
1402
|
+
it("once the budget alerts exist, it lists them as created instead of an empty page", async () => {
|
|
1403
|
+
const created: Array<Monitor> = createdDailyBudgetMonitors();
|
|
1404
|
+
|
|
1405
|
+
expect(created).toHaveLength(2);
|
|
1406
|
+
|
|
1407
|
+
getListMock.mockImplementation((args: unknown) => {
|
|
1408
|
+
const request: {
|
|
1409
|
+
modelType: unknown;
|
|
1410
|
+
query?: { monitorType?: MonitorType } | undefined;
|
|
1411
|
+
} = args as {
|
|
1412
|
+
modelType: unknown;
|
|
1413
|
+
query?: { monitorType?: MonitorType } | undefined;
|
|
1414
|
+
};
|
|
1415
|
+
|
|
1416
|
+
if (request.modelType === Monitor) {
|
|
1417
|
+
return Promise.resolve({
|
|
1418
|
+
data:
|
|
1419
|
+
request.query?.monitorType === MonitorType.Metrics ? created : [],
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
return Promise.resolve(projectList(request.modelType));
|
|
1424
|
+
});
|
|
1425
|
+
|
|
1426
|
+
await followSetUpAlerts(null);
|
|
1427
|
+
|
|
1428
|
+
await waitFor(() => {
|
|
1429
|
+
expect(
|
|
1430
|
+
screen.getByTestId("recommendation-stat-Created"),
|
|
1431
|
+
).toHaveTextContent(/Already created\s*2/);
|
|
1432
|
+
});
|
|
1433
|
+
|
|
1434
|
+
expect(listedTemplateIds()).toEqual([
|
|
1435
|
+
"rum-session-replay-daily-budget-nearly-spent",
|
|
1436
|
+
"rum-session-replay-daily-budget-spent",
|
|
1437
|
+
]);
|
|
1438
|
+
});
|
|
1439
|
+
|
|
1440
|
+
it("a status the page does not know falls back to the alerts still to set up", async () => {
|
|
1441
|
+
await openRecommendations(
|
|
1442
|
+
`/dashboard/${PROJECT_ID}/rum/${APP_ID}/recommendations?search=budget&status=Everything`,
|
|
1443
|
+
null,
|
|
1444
|
+
);
|
|
1445
|
+
|
|
1446
|
+
expect(screen.getByTestId("recommendation-stat-Available")).toHaveAttribute(
|
|
1447
|
+
"aria-pressed",
|
|
1448
|
+
"true",
|
|
1449
|
+
);
|
|
1450
|
+
expect(screen.getByTestId("recommendation-search")).toHaveValue("budget");
|
|
1451
|
+
});
|
|
1452
|
+
|
|
1453
|
+
it("without a status in the link the page opens where it always did", async () => {
|
|
1454
|
+
await openRecommendations(
|
|
1455
|
+
`/dashboard/${PROJECT_ID}/rum/${APP_ID}/recommendations`,
|
|
1456
|
+
null,
|
|
1457
|
+
);
|
|
1458
|
+
|
|
1459
|
+
expect(screen.getByTestId("recommendation-stat-Available")).toHaveAttribute(
|
|
1460
|
+
"aria-pressed",
|
|
1461
|
+
"true",
|
|
1462
|
+
);
|
|
1463
|
+
expect(screen.getByTestId("recommendation-search")).toHaveValue("");
|
|
1464
|
+
});
|
|
1465
|
+
|
|
1466
|
+
it("the word is only a starting point: clearing the box lists the rest", async () => {
|
|
1467
|
+
await followSetUpAlerts(null);
|
|
1468
|
+
|
|
1469
|
+
await waitFor(() => {
|
|
1470
|
+
expect(listedTemplateIds()).toHaveLength(2);
|
|
1471
|
+
});
|
|
1472
|
+
|
|
1473
|
+
fireEvent.change(screen.getByTestId("recommendation-search"), {
|
|
1474
|
+
target: { value: "" },
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
await waitFor(() => {
|
|
1478
|
+
expect(listedTemplateIds()).toContain("rum-poor-lcp");
|
|
1479
|
+
});
|
|
1480
|
+
|
|
1481
|
+
expect(listedTemplateIds()).toContain(
|
|
1482
|
+
"rum-session-replay-daily-budget-spent",
|
|
1483
|
+
);
|
|
1484
|
+
});
|
|
1485
|
+
});
|
|
1486
|
+
|
|
719
1487
|
describe("RecordingHealthDashboardView policy panel", () => {
|
|
720
1488
|
it("reads the policy as the recorder receives it, in labels rather than enum values", () => {
|
|
721
1489
|
renderView(makeSnapshot(makeStatus()));
|