@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
|
@@ -1254,6 +1254,61 @@ describe("DatabaseServerService.findOrCreateByEndpoint", () => {
|
|
|
1254
1254
|
expect(feed.feedInfoInMarkdown).toContain("restored from the archive");
|
|
1255
1255
|
});
|
|
1256
1256
|
|
|
1257
|
+
/*
|
|
1258
|
+
* A lookup that saw no traffic (a resource AI agent registering) returns
|
|
1259
|
+
* the owner as it is: an archived row stays archived, and neither the
|
|
1260
|
+
* endpoint's last match nor the row's engine is touched.
|
|
1261
|
+
*/
|
|
1262
|
+
test("isSighting: false returns the owner without sighting it", async () => {
|
|
1263
|
+
const archived: DatabaseServer = databaseRow({
|
|
1264
|
+
discoverySource: DatabaseServerDiscoverySource.Collector,
|
|
1265
|
+
isArchived: true,
|
|
1266
|
+
autoArchivedAt: new Date("2026-09-01T00:00:00.000Z"),
|
|
1267
|
+
});
|
|
1268
|
+
const owner: any = { databaseServerId: archived.id };
|
|
1269
|
+
findOwner.mockResolvedValue(owner);
|
|
1270
|
+
findOneBy.mockResolvedValue(archived);
|
|
1271
|
+
rawQuery.mockResolvedValue([{ _id: archived.id!.toString() }]);
|
|
1272
|
+
const markMatched: jest.SpyInstance = getJestSpyOn(
|
|
1273
|
+
DatabaseServerEndpointService,
|
|
1274
|
+
"markEndpointMatched",
|
|
1275
|
+
).mockResolvedValue(undefined);
|
|
1276
|
+
const evidence: jest.SpyInstance = getJestSpyOn(
|
|
1277
|
+
service,
|
|
1278
|
+
"applyDatabaseSystemEvidence",
|
|
1279
|
+
).mockResolvedValue(undefined);
|
|
1280
|
+
|
|
1281
|
+
const result: DatabaseServer | null =
|
|
1282
|
+
await DatabaseServerService.findOrCreateByEndpoint({
|
|
1283
|
+
projectId: PROJECT_ID,
|
|
1284
|
+
dbSystem: "postgresql",
|
|
1285
|
+
endpoint: ORDERS_ENDPOINT,
|
|
1286
|
+
discoverySource: DatabaseServerDiscoverySource.Collector,
|
|
1287
|
+
allowCreate: true,
|
|
1288
|
+
isSighting: false,
|
|
1289
|
+
});
|
|
1290
|
+
|
|
1291
|
+
expect(result).toBe(archived);
|
|
1292
|
+
expect(result!.isArchived).toBe(true);
|
|
1293
|
+
expect(result!.autoArchivedAt).toBeDefined();
|
|
1294
|
+
expect(rawQuery).not.toHaveBeenCalled();
|
|
1295
|
+
expect(sideEffects.feed).not.toHaveBeenCalled();
|
|
1296
|
+
expect(markMatched).not.toHaveBeenCalled();
|
|
1297
|
+
expect(evidence).not.toHaveBeenCalled();
|
|
1298
|
+
expect(create).not.toHaveBeenCalled();
|
|
1299
|
+
|
|
1300
|
+
// The same lookup as a sighting restores it.
|
|
1301
|
+
await DatabaseServerService.findOrCreateByEndpoint({
|
|
1302
|
+
projectId: PROJECT_ID,
|
|
1303
|
+
dbSystem: "postgresql",
|
|
1304
|
+
endpoint: ORDERS_ENDPOINT,
|
|
1305
|
+
discoverySource: DatabaseServerDiscoverySource.Collector,
|
|
1306
|
+
allowCreate: true,
|
|
1307
|
+
});
|
|
1308
|
+
expect(markMatched).toHaveBeenCalledWith(owner);
|
|
1309
|
+
expect(rawQuery).toHaveBeenCalledTimes(1);
|
|
1310
|
+
});
|
|
1311
|
+
|
|
1257
1312
|
test("a restore that lost to someone else writes no second feed item", async () => {
|
|
1258
1313
|
const archived: DatabaseServer = databaseRow({
|
|
1259
1314
|
discoverySource: DatabaseServerDiscoverySource.Collector,
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import AnalyticsDatabaseService from "../../../Server/Services/AnalyticsDatabaseService";
|
|
2
|
+
import { LogService } from "../../../Server/Services/LogService";
|
|
3
|
+
import { MetricService } from "../../../Server/Services/MetricService";
|
|
4
|
+
import { SpanService } from "../../../Server/Services/SpanService";
|
|
5
|
+
import { Statement } from "../../../Server/Utils/AnalyticsDatabase/Statement";
|
|
6
|
+
import AnalyticsBaseModel from "../../../Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
|
|
7
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
8
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
9
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
10
|
+
import ServiceType from "../../../Types/Telemetry/ServiceType";
|
|
11
|
+
import SessionReplayBudgetMetricTypeUtil from "../../../Utils/SessionReplay/SessionReplayBudgetMetricType";
|
|
12
|
+
import { describe, expect, test } from "@jest/globals";
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* The metrics billing scan is the only place OneUptime's own session replay
|
|
16
|
+
* budget rows can be told apart from a RUM application's web vitals: both are
|
|
17
|
+
* keyed to the application with primaryEntityType RealUserMonitor, so the
|
|
18
|
+
* (primaryEntityId, primaryEntityType) grouping merges them and only their
|
|
19
|
+
* names differ. These tests pin the SQL the scan generates - the exclusion
|
|
20
|
+
* sits inside the WHERE, binds the names as a parameter, and leaves the
|
|
21
|
+
* statement byte-for-byte unchanged when there is nothing to exclude - with no
|
|
22
|
+
* ClickHouse in the loop, the same way SessionReplayMetering.test.ts pins the
|
|
23
|
+
* replay scan.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const PROJECT_ID: ObjectID = new ObjectID("6512f3a9b7c4d2e108f5a3b9");
|
|
27
|
+
const START_OF_DAY: Date = new Date("2026-09-28T00:00:00.000Z");
|
|
28
|
+
const END_OF_DAY: Date = new Date("2026-09-28T23:59:59.999Z");
|
|
29
|
+
|
|
30
|
+
/*
|
|
31
|
+
* The statement as the scan generated it before excludeNames existed,
|
|
32
|
+
* captured from that code, up to the trailing SETTINGS clause (which belongs
|
|
33
|
+
* to QuerySettingsHelper and is pinned there).
|
|
34
|
+
*/
|
|
35
|
+
const PRE_EXCLUSION_QUERY: string =
|
|
36
|
+
"SELECT primaryEntityId AS primaryEntityId, primaryEntityType AS primaryEntityType, count() AS rowCount, sum(byteSize(*)) AS estimatedBytes FROM {p0:Identifier}.{p1:Identifier} WHERE projectId = {p2:String} AND {p3:Identifier} >= {p4:DateTime64(9)} AND {p5:Identifier} <= {p6:DateTime64(9)} GROUP BY primaryEntityId, primaryEntityType";
|
|
37
|
+
|
|
38
|
+
const BUDGET_METRIC_NAMES: Array<string> =
|
|
39
|
+
SessionReplayBudgetMetricTypeUtil.getAll();
|
|
40
|
+
|
|
41
|
+
type UsageRow = {
|
|
42
|
+
primaryEntityId: string;
|
|
43
|
+
primaryEntityType: string | null;
|
|
44
|
+
rowCount: number;
|
|
45
|
+
estimatedBytes: number;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
interface CapturedScan {
|
|
49
|
+
// Null when the scan refused before reaching ClickHouse.
|
|
50
|
+
query: string | null;
|
|
51
|
+
params: Record<string, unknown> | null;
|
|
52
|
+
result: Array<UsageRow>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* Runs the real groupTelemetryUsageByService with executeQuery replaced, so
|
|
57
|
+
* the generated statement is captured instead of sent. `excludeNames` is
|
|
58
|
+
* passed only when the key is present, so "absent" and "undefined" are
|
|
59
|
+
* separate cases.
|
|
60
|
+
*/
|
|
61
|
+
async function captureUsageScan<TBaseModel extends AnalyticsBaseModel>(
|
|
62
|
+
service: AnalyticsDatabaseService<TBaseModel>,
|
|
63
|
+
options: {
|
|
64
|
+
excludeNames?: Array<string> | undefined;
|
|
65
|
+
responseRows?: Array<JSONObject> | undefined;
|
|
66
|
+
} = {},
|
|
67
|
+
): Promise<CapturedScan> {
|
|
68
|
+
const captured: CapturedScan = { query: null, params: null, result: [] };
|
|
69
|
+
|
|
70
|
+
service.executeQuery = ((statement: Statement): Promise<unknown> => {
|
|
71
|
+
captured.query = statement.query;
|
|
72
|
+
captured.params = statement.query_params;
|
|
73
|
+
|
|
74
|
+
return Promise.resolve({
|
|
75
|
+
json: (): Promise<{ data: Array<JSONObject> }> => {
|
|
76
|
+
return Promise.resolve({ data: options.responseRows || [] });
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}) as unknown as typeof service.executeQuery;
|
|
80
|
+
|
|
81
|
+
captured.result = await service.groupTelemetryUsageByService({
|
|
82
|
+
projectId: PROJECT_ID,
|
|
83
|
+
timestampColumnName: "time",
|
|
84
|
+
startDate: START_OF_DAY,
|
|
85
|
+
endDate: END_OF_DAY,
|
|
86
|
+
...("excludeNames" in options
|
|
87
|
+
? { excludeNames: options.excludeNames }
|
|
88
|
+
: {}),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return captured;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function queryBeforeSettings(query: string | null): string {
|
|
95
|
+
expect(query).not.toBeNull();
|
|
96
|
+
|
|
97
|
+
const settingsIndex: number = query!.indexOf(" SETTINGS ");
|
|
98
|
+
expect(settingsIndex).toBeGreaterThan(-1);
|
|
99
|
+
|
|
100
|
+
return query!.slice(0, settingsIndex);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
describe("groupTelemetryUsageByService without excludeNames", () => {
|
|
104
|
+
test("generates exactly the statement it did before the option existed", async () => {
|
|
105
|
+
const scan: CapturedScan = await captureUsageScan(new MetricService());
|
|
106
|
+
|
|
107
|
+
expect(queryBeforeSettings(scan.query)).toBe(PRE_EXCLUSION_QUERY);
|
|
108
|
+
expect(scan.query).not.toContain("NOT IN");
|
|
109
|
+
|
|
110
|
+
// Same parameters too - p0 is the configured database name.
|
|
111
|
+
expect(Object.keys(scan.params!).sort()).toEqual(
|
|
112
|
+
["p0", "p1", "p2", "p3", "p4", "p5", "p6"].sort(),
|
|
113
|
+
);
|
|
114
|
+
expect(scan.params!["p1"]).toBe("MetricItemV3");
|
|
115
|
+
expect(scan.params!["p2"]).toBe(PROJECT_ID.toString());
|
|
116
|
+
expect(scan.params!["p3"]).toBe("time");
|
|
117
|
+
expect(scan.params!["p5"]).toBe("time");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test.each([
|
|
121
|
+
["an empty list", []],
|
|
122
|
+
["an explicit undefined", undefined],
|
|
123
|
+
])(
|
|
124
|
+
"treats %s as absent: the same statement, the same parameters",
|
|
125
|
+
async (_name: string, excludeNames: Array<string> | undefined) => {
|
|
126
|
+
const absent: CapturedScan = await captureUsageScan(new MetricService());
|
|
127
|
+
const given: CapturedScan = await captureUsageScan(new MetricService(), {
|
|
128
|
+
excludeNames,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
expect(given.query).toBe(absent.query);
|
|
132
|
+
expect(given.params).toEqual(absent.params);
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
test("still scans a table with no name column when there is nothing to exclude", async () => {
|
|
137
|
+
const scan: CapturedScan = await captureUsageScan(new LogService(), {
|
|
138
|
+
excludeNames: [],
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
expect(scan.query).not.toBeNull();
|
|
142
|
+
expect(scan.query).not.toContain("NOT IN");
|
|
143
|
+
expect(scan.params!["p1"]).toBe("LogItemV3");
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe("groupTelemetryUsageByService with excludeNames", () => {
|
|
148
|
+
test("adds `AND name NOT IN` inside the WHERE: after the time window, before GROUP BY", async () => {
|
|
149
|
+
const scan: CapturedScan = await captureUsageScan(new MetricService(), {
|
|
150
|
+
excludeNames: BUDGET_METRIC_NAMES,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const query: string = scan.query!;
|
|
154
|
+
|
|
155
|
+
expect(query).toContain(
|
|
156
|
+
"AND {p5:Identifier} <= {p6:DateTime64(9)} AND name NOT IN {p7:Array(String)} GROUP BY primaryEntityId, primaryEntityType SETTINGS ",
|
|
157
|
+
);
|
|
158
|
+
expect(query.indexOf(" WHERE ")).toBeLessThan(query.indexOf("NOT IN"));
|
|
159
|
+
expect(query.indexOf("NOT IN")).toBeLessThan(query.indexOf(" GROUP BY "));
|
|
160
|
+
expect(query.indexOf(" GROUP BY ")).toBeLessThan(
|
|
161
|
+
query.indexOf(" SETTINGS "),
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
// Exactly one exclusion clause, and nothing else about the scan moved.
|
|
165
|
+
expect(query.split("NOT IN")).toHaveLength(2);
|
|
166
|
+
expect(
|
|
167
|
+
queryBeforeSettings(query).replace(
|
|
168
|
+
" AND name NOT IN {p7:Array(String)}",
|
|
169
|
+
"",
|
|
170
|
+
),
|
|
171
|
+
).toBe(PRE_EXCLUSION_QUERY);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("binds every name, in order, as one Array(String) parameter", async () => {
|
|
175
|
+
const scan: CapturedScan = await captureUsageScan(new MetricService(), {
|
|
176
|
+
excludeNames: BUDGET_METRIC_NAMES,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
expect(scan.params!["p7"]).toEqual(BUDGET_METRIC_NAMES);
|
|
180
|
+
|
|
181
|
+
// The names never reach the SQL text itself.
|
|
182
|
+
for (const name of BUDGET_METRIC_NAMES) {
|
|
183
|
+
expect(scan.query).not.toContain(name);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("keeps a hostile name a parameter value, never SQL", async () => {
|
|
188
|
+
const hostileName: string = "x') OR 1=1 GROUP BY name --";
|
|
189
|
+
|
|
190
|
+
const scan: CapturedScan = await captureUsageScan(new MetricService(), {
|
|
191
|
+
excludeNames: [hostileName],
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
expect(scan.params!["p7"]).toEqual([hostileName]);
|
|
195
|
+
expect(scan.query).not.toContain("OR 1=1");
|
|
196
|
+
expect(scan.query).not.toContain("--");
|
|
197
|
+
expect(scan.query!.split("GROUP BY")).toHaveLength(2);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("leaves the caller's list untouched", async () => {
|
|
201
|
+
const excludeNames: Array<string> = [...BUDGET_METRIC_NAMES];
|
|
202
|
+
|
|
203
|
+
await captureUsageScan(new MetricService(), { excludeNames });
|
|
204
|
+
|
|
205
|
+
expect(excludeNames).toEqual(BUDGET_METRIC_NAMES);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test("parses the grouped rows exactly as before", async () => {
|
|
209
|
+
const appId: string = ObjectID.generate().toString();
|
|
210
|
+
|
|
211
|
+
const scan: CapturedScan = await captureUsageScan(new MetricService(), {
|
|
212
|
+
excludeNames: BUDGET_METRIC_NAMES,
|
|
213
|
+
responseRows: [
|
|
214
|
+
{
|
|
215
|
+
primaryEntityId: appId,
|
|
216
|
+
primaryEntityType: ServiceType.RealUserMonitor,
|
|
217
|
+
rowCount: "12",
|
|
218
|
+
estimatedBytes: "3456",
|
|
219
|
+
},
|
|
220
|
+
// Unattributed rows are skipped, as they always were.
|
|
221
|
+
{
|
|
222
|
+
primaryEntityId: "",
|
|
223
|
+
primaryEntityType: ServiceType.OpenTelemetry,
|
|
224
|
+
rowCount: "1",
|
|
225
|
+
estimatedBytes: "1",
|
|
226
|
+
},
|
|
227
|
+
// A blank type reads as a legacy row.
|
|
228
|
+
{
|
|
229
|
+
primaryEntityId: PROJECT_ID.toString(),
|
|
230
|
+
primaryEntityType: " ",
|
|
231
|
+
rowCount: 2,
|
|
232
|
+
estimatedBytes: 0,
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
expect(scan.result).toEqual([
|
|
238
|
+
{
|
|
239
|
+
primaryEntityId: appId,
|
|
240
|
+
primaryEntityType: ServiceType.RealUserMonitor,
|
|
241
|
+
rowCount: 12,
|
|
242
|
+
estimatedBytes: 3456,
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
primaryEntityId: PROJECT_ID.toString(),
|
|
246
|
+
primaryEntityType: null,
|
|
247
|
+
rowCount: 2,
|
|
248
|
+
estimatedBytes: 0,
|
|
249
|
+
},
|
|
250
|
+
]);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("refuses a table with no name column before querying it", async () => {
|
|
254
|
+
const service: LogService = new LogService();
|
|
255
|
+
let executed: boolean = false;
|
|
256
|
+
service.executeQuery = (async (): Promise<never> => {
|
|
257
|
+
executed = true;
|
|
258
|
+
throw new Error("must not run");
|
|
259
|
+
}) as unknown as typeof service.executeQuery;
|
|
260
|
+
|
|
261
|
+
await expect(
|
|
262
|
+
service.groupTelemetryUsageByService({
|
|
263
|
+
projectId: PROJECT_ID,
|
|
264
|
+
timestampColumnName: "time",
|
|
265
|
+
startDate: START_OF_DAY,
|
|
266
|
+
endDate: END_OF_DAY,
|
|
267
|
+
excludeNames: ["anything"],
|
|
268
|
+
}),
|
|
269
|
+
).rejects.toThrow(BadDataException);
|
|
270
|
+
expect(executed).toBe(false);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("refuses a Nullable name column, where NOT IN would silently drop every unnamed row", async () => {
|
|
274
|
+
/*
|
|
275
|
+
* Span.name is optional. ClickHouse does not treat `NULL NOT IN (...)`
|
|
276
|
+
* as true, so the scan would lose every span without a name from the
|
|
277
|
+
* bill - an undercount nobody would notice.
|
|
278
|
+
*/
|
|
279
|
+
const service: SpanService = new SpanService();
|
|
280
|
+
let executed: boolean = false;
|
|
281
|
+
service.executeQuery = (async (): Promise<never> => {
|
|
282
|
+
executed = true;
|
|
283
|
+
throw new Error("must not run");
|
|
284
|
+
}) as unknown as typeof service.executeQuery;
|
|
285
|
+
|
|
286
|
+
await expect(
|
|
287
|
+
service.groupTelemetryUsageByService({
|
|
288
|
+
projectId: PROJECT_ID,
|
|
289
|
+
timestampColumnName: "startTime",
|
|
290
|
+
startDate: START_OF_DAY,
|
|
291
|
+
endDate: END_OF_DAY,
|
|
292
|
+
excludeNames: ["anything"],
|
|
293
|
+
}),
|
|
294
|
+
).rejects.toThrow(
|
|
295
|
+
"excludeNames needs a required name column, and SpanItemV3 has none",
|
|
296
|
+
);
|
|
297
|
+
expect(executed).toBe(false);
|
|
298
|
+
});
|
|
299
|
+
});
|
|
@@ -228,6 +228,50 @@ describe("HostService.findOrCreateByHostIdentifier L1 memo", () => {
|
|
|
228
228
|
expect(findOneBy).toHaveBeenCalledTimes(2);
|
|
229
229
|
});
|
|
230
230
|
|
|
231
|
+
/*
|
|
232
|
+
* A host deleted within the TTL keeps resolving to its old id from the
|
|
233
|
+
* memo; a caller that found that id gone (a resource AI agent
|
|
234
|
+
* registering again right after the delete) asks past the memo, gets the
|
|
235
|
+
* host created anew, and the memo then holds the new id.
|
|
236
|
+
*/
|
|
237
|
+
test("bypassMemo looks the host up again and refreshes the memo with what it found", async () => {
|
|
238
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
239
|
+
const deleted: Host = buildHost();
|
|
240
|
+
const recreated: Host = buildHost();
|
|
241
|
+
findOneBy.mockResolvedValue(deleted);
|
|
242
|
+
|
|
243
|
+
await HostService.findOrCreateByHostIdentifier({
|
|
244
|
+
projectId,
|
|
245
|
+
hostIdentifier: "web-01",
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// The host is deleted: nothing owns the identifier any more.
|
|
249
|
+
findOneBy.mockResolvedValue(null);
|
|
250
|
+
create.mockResolvedValue(recreated);
|
|
251
|
+
|
|
252
|
+
const memoed: Host = await HostService.findOrCreateByHostIdentifier({
|
|
253
|
+
projectId,
|
|
254
|
+
hostIdentifier: "web-01",
|
|
255
|
+
});
|
|
256
|
+
expect(memoed._id).toBe(deleted._id);
|
|
257
|
+
|
|
258
|
+
const fresh: Host = await HostService.findOrCreateByHostIdentifier({
|
|
259
|
+
projectId,
|
|
260
|
+
hostIdentifier: "web-01",
|
|
261
|
+
bypassMemo: true,
|
|
262
|
+
});
|
|
263
|
+
expect(fresh._id).toBe(recreated._id);
|
|
264
|
+
expect(findOneBy).toHaveBeenCalledTimes(2);
|
|
265
|
+
expect(create).toHaveBeenCalledTimes(1);
|
|
266
|
+
|
|
267
|
+
const afterwards: Host = await HostService.findOrCreateByHostIdentifier({
|
|
268
|
+
projectId,
|
|
269
|
+
hostIdentifier: "web-01",
|
|
270
|
+
});
|
|
271
|
+
expect(afterwards._id).toBe(recreated._id);
|
|
272
|
+
expect(findOneBy).toHaveBeenCalledTimes(2);
|
|
273
|
+
});
|
|
274
|
+
|
|
231
275
|
test("memoizes a host it just created", async () => {
|
|
232
276
|
const projectId: ObjectID = ObjectID.generate();
|
|
233
277
|
const created: Host = buildHost({ hostIdentifier: "brand-new" });
|
|
@@ -73,6 +73,7 @@ function minutesBetween(from: Date, to: Date): number {
|
|
|
73
73
|
describe("claimMonitorProbesForProbing computes the schedule the customer asked for", () => {
|
|
74
74
|
afterEach(() => {
|
|
75
75
|
jest.restoreAllMocks();
|
|
76
|
+
jest.useRealTimers();
|
|
76
77
|
});
|
|
77
78
|
|
|
78
79
|
describe("the production scenario", () => {
|
|
@@ -114,13 +115,51 @@ describe("claimMonitorProbesForProbing computes the schedule the customer asked
|
|
|
114
115
|
},
|
|
115
116
|
);
|
|
116
117
|
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
/*
|
|
119
|
+
* "1 day" means the next midnight, and on the real clock "more than a
|
|
120
|
+
* minute away" cannot tell that from the old one-minute fallback in the
|
|
121
|
+
* last minute of the day: at 23:59:14 the right answer IS under a minute
|
|
122
|
+
* away (this failed CI at exactly that time), and "+1 minute" lands on
|
|
123
|
+
* 00:00 too. So the clock is pinned - only Date, the harness's promises
|
|
124
|
+
* run as usual - once mid-day and once in that last minute, and the
|
|
125
|
+
* schedule must be the next midnight to the millisecond, which "+1
|
|
126
|
+
* minute" never is: it keeps the seconds.
|
|
127
|
+
*/
|
|
128
|
+
test.each([
|
|
129
|
+
["mid-day", new Date(2026, 8, 29, 10, 17, 42, 123)],
|
|
130
|
+
["in the last minute of the day", new Date(2026, 8, 29, 23, 59, 14, 500)],
|
|
131
|
+
])(
|
|
132
|
+
'a monitor storing "1 day" is scheduled for the next midnight, not a minute away (%s)',
|
|
133
|
+
async (_when: string, pinnedNow: Date) => {
|
|
134
|
+
jest.useFakeTimers({
|
|
135
|
+
now: pinnedNow,
|
|
136
|
+
doNotFake: [
|
|
137
|
+
"hrtime",
|
|
138
|
+
"nextTick",
|
|
139
|
+
"performance",
|
|
140
|
+
"queueMicrotask",
|
|
141
|
+
"requestAnimationFrame",
|
|
142
|
+
"cancelAnimationFrame",
|
|
143
|
+
"requestIdleCallback",
|
|
144
|
+
"cancelIdleCallback",
|
|
145
|
+
"setImmediate",
|
|
146
|
+
"clearImmediate",
|
|
147
|
+
"setInterval",
|
|
148
|
+
"clearInterval",
|
|
149
|
+
"setTimeout",
|
|
150
|
+
"clearTimeout",
|
|
151
|
+
],
|
|
152
|
+
});
|
|
119
153
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
154
|
+
const { nextPingAt, now } = await claimAndReadNextPingAt("1 day");
|
|
155
|
+
|
|
156
|
+
expect(now.getTime()).toBe(pinnedNow.getTime());
|
|
157
|
+
expect(nextPingAt.getTime()).toBe(
|
|
158
|
+
new Date(2026, 8, 30, 0, 0, 0, 0).getTime(),
|
|
159
|
+
);
|
|
160
|
+
expect(nextPingAt.getTime()).not.toBe(pinnedNow.getTime() + 60 * 1000);
|
|
161
|
+
},
|
|
162
|
+
);
|
|
124
163
|
});
|
|
125
164
|
|
|
126
165
|
describe("values that were always valid are unaffected", () => {
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import DatabaseBaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
2
|
+
import MetricRecordingRule from "../../../Models/DatabaseModels/MetricRecordingRule";
|
|
3
|
+
import TraceRecordingRule from "../../../Models/DatabaseModels/TraceRecordingRule";
|
|
4
|
+
import MetricRecordingRuleService, {
|
|
5
|
+
Service as MetricRecordingRuleServiceClass,
|
|
6
|
+
} from "../../../Server/Services/MetricRecordingRuleService";
|
|
7
|
+
import TraceRecordingRuleService from "../../../Server/Services/TraceRecordingRuleService";
|
|
8
|
+
import CreateBy from "../../../Server/Types/Database/CreateBy";
|
|
9
|
+
import UpdateBy from "../../../Server/Types/Database/UpdateBy";
|
|
10
|
+
import { OnCreate, OnUpdate } from "../../../Server/Types/Database/Hooks";
|
|
11
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
12
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
13
|
+
import SessionReplayBudgetMetricType from "../../../Types/Rum/SessionReplayBudgetMetricType";
|
|
14
|
+
import { describe, expect, test } from "@jest/globals";
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
* Recording rules write their output straight into the metric store, past
|
|
18
|
+
* OTLP ingest - which is where every other customer-chosen metric name is
|
|
19
|
+
* checked against the reserved session replay namespace. The budget series
|
|
20
|
+
* under it open incidents and are left out of telemetry billing by name, so
|
|
21
|
+
* a rule must not be able to write one either: both services refuse a
|
|
22
|
+
* reserved output name when a rule is created, and when an update sets it.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
interface Hooks<TModel extends DatabaseBaseModel> {
|
|
26
|
+
onBeforeCreate: (createBy: CreateBy<TModel>) => Promise<OnCreate<TModel>>;
|
|
27
|
+
onBeforeUpdate: (updateBy: UpdateBy<TModel>) => Promise<OnUpdate<TModel>>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const RESERVED_NAMES: Array<string> = [
|
|
31
|
+
...Object.values(SessionReplayBudgetMetricType),
|
|
32
|
+
"oneuptime.rum.session.replay.anything.new",
|
|
33
|
+
"OneUptime.RUM.Session.Replay.Budget.Project.Daily.Used.Percent",
|
|
34
|
+
" oneuptime.rum.session.replay.budget.project.daily.used.bytes ",
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const ALLOWED_NAMES: Array<string> = [
|
|
38
|
+
"web_vital.lcp",
|
|
39
|
+
"checkout.latency.p99",
|
|
40
|
+
"oneuptime.llm.budget.percent.used",
|
|
41
|
+
"oneuptime.rum.application.id",
|
|
42
|
+
"oneuptime.rum.session.replayed",
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
interface ServiceCase {
|
|
46
|
+
name: string;
|
|
47
|
+
hooks: Hooks<MetricRecordingRule | TraceRecordingRule>;
|
|
48
|
+
newRule: () => MetricRecordingRule | TraceRecordingRule;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const SERVICES: Array<ServiceCase> = [
|
|
52
|
+
{
|
|
53
|
+
name: "MetricRecordingRuleService",
|
|
54
|
+
hooks: MetricRecordingRuleService as unknown as Hooks<
|
|
55
|
+
MetricRecordingRule | TraceRecordingRule
|
|
56
|
+
>,
|
|
57
|
+
newRule: (): MetricRecordingRule => {
|
|
58
|
+
return new MetricRecordingRule();
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "TraceRecordingRuleService",
|
|
63
|
+
hooks: TraceRecordingRuleService as unknown as Hooks<
|
|
64
|
+
MetricRecordingRule | TraceRecordingRule
|
|
65
|
+
>,
|
|
66
|
+
newRule: (): TraceRecordingRule => {
|
|
67
|
+
return new TraceRecordingRule();
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
function createBy(
|
|
73
|
+
rule: MetricRecordingRule | TraceRecordingRule,
|
|
74
|
+
): CreateBy<MetricRecordingRule | TraceRecordingRule> {
|
|
75
|
+
return {
|
|
76
|
+
data: rule,
|
|
77
|
+
props: { isRoot: true },
|
|
78
|
+
} as unknown as CreateBy<MetricRecordingRule | TraceRecordingRule>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function updateBy(
|
|
82
|
+
data: Record<string, unknown>,
|
|
83
|
+
): UpdateBy<MetricRecordingRule | TraceRecordingRule> {
|
|
84
|
+
return {
|
|
85
|
+
query: { _id: ObjectID.generate().toString() },
|
|
86
|
+
data: data,
|
|
87
|
+
props: { isRoot: true },
|
|
88
|
+
} as unknown as UpdateBy<MetricRecordingRule | TraceRecordingRule>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
describe("MetricRecordingRuleService.assertOutputMetricNameAllowed", () => {
|
|
92
|
+
test.each(RESERVED_NAMES)("refuses %p", (name: string) => {
|
|
93
|
+
expect(() => {
|
|
94
|
+
MetricRecordingRuleServiceClass.assertOutputMetricNameAllowed(name);
|
|
95
|
+
}).toThrow(BadDataException);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("says which prefix is reserved and what to do", () => {
|
|
99
|
+
expect(() => {
|
|
100
|
+
MetricRecordingRuleServiceClass.assertOutputMetricNameAllowed(
|
|
101
|
+
SessionReplayBudgetMetricType.ProjectDailyUsedPercent,
|
|
102
|
+
);
|
|
103
|
+
}).toThrow(
|
|
104
|
+
'Metric names that start with "oneuptime.rum.session.replay." are reserved for OneUptime\'s session replay budget metrics. Choose another output metric name.',
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test.each(ALLOWED_NAMES)("allows %p", (name: string) => {
|
|
109
|
+
expect(() => {
|
|
110
|
+
MetricRecordingRuleServiceClass.assertOutputMetricNameAllowed(name);
|
|
111
|
+
}).not.toThrow();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test.each([undefined, null, "", 42])(
|
|
115
|
+
"leaves a missing or non-string name %p to the model's own validation",
|
|
116
|
+
(name: unknown) => {
|
|
117
|
+
expect(() => {
|
|
118
|
+
MetricRecordingRuleServiceClass.assertOutputMetricNameAllowed(name);
|
|
119
|
+
}).not.toThrow();
|
|
120
|
+
},
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe.each(SERVICES)("$name", ({ hooks, newRule }: ServiceCase) => {
|
|
125
|
+
test("refuses to create a rule whose output name is reserved", async () => {
|
|
126
|
+
const rule: MetricRecordingRule | TraceRecordingRule = newRule();
|
|
127
|
+
rule.outputMetricName =
|
|
128
|
+
SessionReplayBudgetMetricType.ApplicationMonthlyUsedPercent;
|
|
129
|
+
|
|
130
|
+
await expect(hooks.onBeforeCreate(createBy(rule))).rejects.toThrow(
|
|
131
|
+
BadDataException,
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("creates a rule with any other output name, untouched", async () => {
|
|
136
|
+
const rule: MetricRecordingRule | TraceRecordingRule = newRule();
|
|
137
|
+
rule.outputMetricName = "checkout.latency.p99";
|
|
138
|
+
|
|
139
|
+
const result: OnCreate<MetricRecordingRule | TraceRecordingRule> =
|
|
140
|
+
await hooks.onBeforeCreate(createBy(rule));
|
|
141
|
+
|
|
142
|
+
expect(result.createBy.data.outputMetricName).toBe("checkout.latency.p99");
|
|
143
|
+
expect(result.carryForward).toBeNull();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("refuses an update that renames the output into the reserved namespace", async () => {
|
|
147
|
+
await expect(
|
|
148
|
+
hooks.onBeforeUpdate(
|
|
149
|
+
updateBy({
|
|
150
|
+
outputMetricName: "oneuptime.rum.session.replay.budget.mine",
|
|
151
|
+
}),
|
|
152
|
+
),
|
|
153
|
+
).rejects.toThrow(BadDataException);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("lets through an update that does not touch the output name", async () => {
|
|
157
|
+
const result: OnUpdate<MetricRecordingRule | TraceRecordingRule> =
|
|
158
|
+
await hooks.onBeforeUpdate(updateBy({ isEnabled: false }));
|
|
159
|
+
|
|
160
|
+
expect(result.carryForward).toBeNull();
|
|
161
|
+
expect(
|
|
162
|
+
(result.updateBy.data as unknown as Record<string, unknown>)["isEnabled"],
|
|
163
|
+
).toBe(false);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("lets through an update to an allowed output name", async () => {
|
|
167
|
+
await expect(
|
|
168
|
+
hooks.onBeforeUpdate(updateBy({ outputMetricName: "web_vital.lcp.p75" })),
|
|
169
|
+
).resolves.toBeDefined();
|
|
170
|
+
});
|
|
171
|
+
});
|