@oneuptime/common 12.0.4 → 12.0.5
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/AIRun.ts +38 -1
- package/Models/DatabaseModels/AlertFeed.ts +30 -0
- package/Models/DatabaseModels/Dashboard.ts +28 -0
- package/Models/DatabaseModels/Incident.ts +2 -0
- package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
- package/Models/DatabaseModels/IncidentFeed.ts +30 -0
- package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
- package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
- package/Models/DatabaseModels/LogSavedView.ts +33 -0
- package/Models/DatabaseModels/Project.ts +231 -9
- package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.ts +4 -4
- package/Models/DatabaseModels/StatusPage.ts +28 -0
- package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
- package/Server/API/AIAgentDataAPI.ts +44 -55
- package/Server/API/AIInvestigationAPI.ts +175 -17
- package/Server/API/DashboardAPI.ts +10 -10
- package/Server/API/StatusPageAPI.ts +38 -19
- package/Server/EnvironmentConfig.ts +16 -0
- package/Server/Infrastructure/ClickhouseConfig.ts +15 -1
- package/Server/Infrastructure/ClickhouseDatabase.ts +1 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.ts +27 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.ts +89 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.ts +27 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.ts +26 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
- package/Server/Services/AIRunService.ts +78 -10
- package/Server/Services/AIService.ts +94 -22
- package/Server/Services/AlertFeedService.ts +5 -0
- package/Server/Services/AnalyticsDatabaseService.ts +29 -0
- package/Server/Services/DatabaseService.ts +43 -2
- package/Server/Services/IncidentFeedService.ts +5 -0
- package/Server/Services/LlmLogService.ts +46 -2
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +6 -0
- package/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.ts +4 -0
- package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +8 -5
- package/Server/Utils/AI/CodeFix/FixRunBudget.ts +90 -16
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +2 -0
- package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +2 -0
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +223 -22
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +25 -24
- package/Server/Utils/AI/SRE/ConfidenceSignal.ts +182 -71
- package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +252 -43
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +25 -24
- package/Server/Utils/AI/SRE/InstrumentationTaskTrigger.ts +47 -13
- package/Server/Utils/AI/SRE/InvestigationGrader.ts +9 -5
- package/Server/Utils/AI/SRE/InvestigationQueue.ts +141 -24
- package/Server/Utils/AI/SRE/InvestigationSubjectLock.ts +66 -0
- package/Server/Utils/AI/SRE/PostedRootCause.ts +94 -3
- package/Server/Utils/AI/SRE/README.md +1 -1
- package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +132 -10
- package/Server/Utils/AnalyticsDatabase/ClusterConfig.ts +24 -0
- package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +29 -0
- package/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.ts +56 -0
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +46 -0
- package/Server/Utils/Monitor/MonitorTemplateUtil.ts +1 -0
- package/Server/Utils/PasswordHash.ts +28 -4
- package/Tests/App/Dashboard/AIInvestigationHeaderStatus.test.tsx +251 -0
- package/Tests/App/Dashboard/EventStatusPanel.test.tsx +747 -0
- package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +480 -0
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1184 -0
- package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +500 -0
- package/Tests/App/Dashboard/PortMonitorCriteriaFilter.test.ts +93 -0
- package/Tests/App/StatusPage/PublicStatusPageAPIErrorHandling.test.ts +117 -0
- package/Tests/App/StatusPage/StatusPageModelAPIPolymorphism.test.ts +223 -0
- package/Tests/Models/DatabaseModels/PermissionCatalogueCoverage.test.ts +243 -0
- package/Tests/Server/API/AIAgentDataFixFromIncidentContext.test.ts +266 -0
- package/Tests/Server/API/AIInvestigationAPI.test.ts +594 -0
- package/Tests/Server/API/AIInvestigationCreateFixTask.test.ts +80 -0
- package/Tests/Server/API/DashboardMasterPasswordAPI.test.ts +172 -1
- package/Tests/Server/API/StatusPageMasterPasswordAPI.test.ts +335 -0
- package/Tests/Server/Services/AIRunCodeFixClaim.test.ts +59 -5
- package/Tests/Server/Services/AIRunHumanVerdict.test.ts +127 -58
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +396 -20
- package/Tests/Server/Services/AddMasterPasswordSaltMigration.test.ts +283 -0
- package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +25 -1
- package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +45 -35
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsWithoutHooks.test.ts +54 -0
- package/Tests/Server/Services/FeedAIRunAssociation.test.ts +125 -0
- package/Tests/Server/Services/FixFromIncidentTaskTrigger.test.ts +418 -57
- package/Tests/Server/Services/InstrumentationTaskTrigger.test.ts +173 -14
- package/Tests/Server/Services/MasterPasswordScrypt.test.ts +567 -0
- package/Tests/Server/Services/SeparateIncidentAlertAiSettingsMigration.test.ts +194 -0
- package/Tests/Server/Utils/AI/AIAlertGating.test.ts +37 -4
- package/Tests/Server/Utils/AI/AIConfidenceSignal.test.ts +330 -29
- package/Tests/Server/Utils/AI/AIIncidentGating.test.ts +25 -0
- package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +293 -5
- package/Tests/Server/Utils/AI/CodeFixAgentCompletion.test.ts +59 -1
- package/Tests/Server/Utils/AI/FixFromIncidentAutoTrigger.test.ts +342 -19
- package/Tests/Server/Utils/AI/FixRunBudget.test.ts +214 -30
- package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +2 -0
- package/Tests/Server/Utils/AI/InvestigationGrader.test.ts +65 -17
- package/Tests/Server/Utils/AI/InvestigationQueueRemediationExecution.test.ts +3 -0
- package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +313 -20
- package/Tests/Server/Utils/AI/PostedRootCause.test.ts +254 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +4 -0
- package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +2 -0
- package/Tests/Server/Utils/AI/SubjectCodeFixRunDedupe.test.ts +44 -0
- package/Tests/Server/Utils/AnalyticsDatabase/ClusterAwareSchema.test.ts +30 -0
- package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaPortTimings.test.ts +212 -0
- package/Tests/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.test.ts +401 -0
- package/Tests/Server/Utils/Monitor/MonitorMetricUtilPortTimings.test.ts +243 -0
- package/Tests/Server/Utils/PasswordHash.test.ts +30 -0
- package/Tests/Types/Dashboard/DashboardTemplates.test.ts +111 -0
- package/Tests/Types/Monitor/CephMetricCatalog.test.ts +104 -0
- package/Tests/Types/Monitor/CriteriaFilter.test.ts +4 -0
- package/Tests/Types/Monitor/DockerMetricCatalog.test.ts +109 -0
- package/Tests/Types/Monitor/DockerSwarmMetricCatalog.test.ts +118 -0
- package/Tests/Types/Monitor/HostMetricCatalog.test.ts +104 -0
- package/Tests/Types/Monitor/IotMetricCatalog.test.ts +104 -0
- package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +118 -0
- package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +42 -0
- package/Tests/Types/Monitor/MonitorStep.test.ts +38 -0
- package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +75 -0
- package/Tests/Types/Monitor/PodmanMetricCatalog.test.ts +109 -0
- package/Tests/Types/Monitor/ProxmoxMetricCatalog.test.ts +107 -0
- package/Tests/Types/Monitor/SnmpMonitor/SnmpVendorTemplate.test.ts +151 -0
- package/Tests/Types/Permission.test.ts +137 -0
- package/Tests/Types/Rum/SessionReplayMaskingMode.test.ts +101 -0
- package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +5 -5
- package/Tests/UI/Components/CardModelDetailEdit.test.tsx +3 -5
- package/Tests/UI/Components/ErrorBoundary.test.tsx +158 -0
- package/Tests/UI/Components/FeedItemSafeMode.test.tsx +74 -0
- package/Tests/UI/Components/MoreMenu.test.tsx +756 -0
- package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +62 -0
- package/Tests/UI/Monitor/PortMonitorView.test.tsx +237 -0
- package/Tests/UI/Telemetry/TelemetrySnapshotWindowAlert.test.tsx +91 -0
- package/Tests/UI/Utils/DownloadFile.test.ts +103 -0
- package/Tests/UI/Utils/ErrorSupportBundle.test.ts +582 -0
- package/Tests/UI/Utils/Project.test.ts +31 -0
- package/Tests/UI/Utils/StatusPageModelAPIInjection.test.ts +245 -0
- package/Tests/UI/Utils/UseDashboardGridDnd.test.tsx +820 -0
- package/Tests/Utils/Dashboard/DashboardViewConfig.test.ts +329 -0
- package/Tests/Utils/Dashboard/GridLayout.test.ts +951 -0
- package/Tests/Utils/Monitor/MonitorMetricType.test.ts +117 -0
- package/Tests/Utils/RecordingRuleExpression.test.ts +327 -0
- package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +186 -0
- package/Tests/Utils/StatusPage/GroupTree.test.ts +456 -0
- package/Tests/Utils/StatusPage/OverviewGroupHierarchyVisibility.test.ts +588 -0
- package/Tests/Utils/Telemetry/SavedViewTimeRange.test.ts +244 -0
- package/Tests/Utils/Telemetry/TelemetryQueryTimeRange.test.ts +825 -0
- package/Types/AI/AIRunCodeFixRecommendation.ts +17 -0
- package/Types/AI/CodeFixTaskContext.ts +63 -3
- package/Types/AI/CodeFixTaskType.ts +3 -3
- package/Types/HashedString.ts +4 -3
- package/Types/Monitor/CriteriaFilter.ts +4 -0
- package/Types/Monitor/DomainMonitor/DomainLookupMethod.ts +23 -0
- package/Types/Monitor/DomainMonitor/DomainMonitorResponse.ts +7 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +24 -7
- package/Types/Monitor/MonitorMetricType.ts +8 -0
- package/Types/Monitor/MonitorStep.ts +10 -1
- package/Types/Monitor/MonitorStepDomainMonitor.ts +21 -0
- package/Types/Monitor/PortMonitor/PortMonitorTimings.ts +10 -0
- package/Types/Permission.ts +23 -175
- package/Types/Probe/ProbeMonitorResponse.ts +6 -0
- package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +2 -3
- package/UI/Components/ErrorBoundary.tsx +138 -11
- package/UI/Components/Feed/FeedItem.tsx +9 -2
- package/UI/Components/ModelDetail/CardModelDetail.tsx +0 -12
- package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +12 -2
- package/UI/Components/MoreMenu/MoreMenu.tsx +285 -97
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +8 -5
- package/UI/Utils/DownloadFile.ts +16 -8
- package/UI/Utils/ErrorSupportBundle.ts +805 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +19 -8
- package/UI/Utils/Project.ts +4 -0
- package/UI/Utils/StatusPage.ts +8 -2
- package/UI/Utils/UseDashboardGridDnd.ts +917 -0
- package/Utils/Dashboard/DashboardViewConfig.ts +80 -36
- package/Utils/Dashboard/GridLayout.ts +523 -0
- package/Utils/Metrics/RecordingRuleExpression.ts +19 -17
- package/Utils/Monitor/MonitorMetricType.ts +44 -2
- package/Utils/StatusPage/GroupNestingLayout.ts +69 -0
- package/Utils/StatusPage/GroupTree.ts +157 -66
- package/Utils/StatusPage/ResourceUptime.ts +37 -2
- package/Utils/Telemetry/SavedViewTimeRange.ts +114 -0
- package/Utils/Telemetry/TelemetryQueryTimeRange.ts +334 -0
- package/build/dist/Models/DatabaseModels/AIRun.js +39 -1
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertFeed.js +32 -0
- package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Dashboard.js +29 -0
- package/build/dist/Models/DatabaseModels/Dashboard.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +2 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentFeed.js +32 -0
- package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LogSavedView.js +33 -0
- package/build/dist/Models/DatabaseModels/LogSavedView.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +241 -11
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.js +4 -4
- package/build/dist/Models/DatabaseModels/StatusPage.js +29 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
- package/build/dist/Server/API/AIAgentDataAPI.js +28 -46
- package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +111 -11
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +8 -5
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +29 -11
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +14 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js +12 -1
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseDatabase.js +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseDatabase.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js +36 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +58 -12
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +74 -21
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertFeedService.js +3 -0
- package/build/dist/Server/Services/AlertFeedService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +29 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +21 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentFeedService.js +3 -0
- package/build/dist/Server/Services/IncidentFeedService.js.map +1 -1
- package/build/dist/Server/Services/LlmLogService.js +22 -1
- package/build/dist/Server/Services/LlmLogService.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +3 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js +4 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +8 -5
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +63 -24
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +2 -0
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +2 -0
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +156 -28
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +21 -19
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js +128 -49
- package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js +170 -31
- package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +21 -19
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js +33 -9
- package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +9 -5
- package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +121 -41
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js +46 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js +61 -14
- package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +79 -1
- package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js +21 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +20 -3
- package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js +43 -0
- package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +33 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
- package/build/dist/Server/Utils/PasswordHash.js +25 -4
- package/build/dist/Server/Utils/PasswordHash.js.map +1 -1
- package/build/dist/Types/AI/AIRunCodeFixRecommendation.js +18 -0
- package/build/dist/Types/AI/AIRunCodeFixRecommendation.js.map +1 -0
- package/build/dist/Types/AI/CodeFixTaskContext.js +23 -3
- package/build/dist/Types/AI/CodeFixTaskContext.js.map +1 -1
- package/build/dist/Types/AI/CodeFixTaskType.js +3 -3
- package/build/dist/Types/HashedString.js +4 -3
- package/build/dist/Types/HashedString.js.map +1 -1
- package/build/dist/Types/Monitor/CriteriaFilter.js +4 -0
- package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
- package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js +24 -0
- package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +24 -7
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorMetricType.js +7 -0
- package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +6 -1
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js +15 -0
- package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js.map +1 -1
- package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js +2 -0
- package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js.map +1 -0
- package/build/dist/Types/Permission.js +23 -155
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +2 -5
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
- package/build/dist/UI/Components/ErrorBoundary.js +67 -8
- package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
- package/build/dist/UI/Components/Feed/FeedItem.js +2 -2
- package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +0 -12
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +12 -2
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +193 -65
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Utils/DownloadFile.js +15 -8
- package/build/dist/UI/Utils/DownloadFile.js.map +1 -1
- package/build/dist/UI/Utils/ErrorSupportBundle.js +476 -0
- package/build/dist/UI/Utils/ErrorSupportBundle.js.map +1 -0
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -8
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/Project.js +3 -0
- package/build/dist/UI/Utils/Project.js.map +1 -1
- package/build/dist/UI/Utils/StatusPage.js +4 -4
- package/build/dist/UI/Utils/StatusPage.js.map +1 -1
- package/build/dist/UI/Utils/UseDashboardGridDnd.js +551 -0
- package/build/dist/UI/Utils/UseDashboardGridDnd.js.map +1 -0
- package/build/dist/Utils/Dashboard/DashboardViewConfig.js +42 -26
- package/build/dist/Utils/Dashboard/DashboardViewConfig.js.map +1 -1
- package/build/dist/Utils/Dashboard/GridLayout.js +338 -0
- package/build/dist/Utils/Dashboard/GridLayout.js.map +1 -0
- package/build/dist/Utils/Metrics/RecordingRuleExpression.js +19 -17
- package/build/dist/Utils/Metrics/RecordingRuleExpression.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js +38 -6
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Utils/StatusPage/GroupNestingLayout.js +46 -0
- package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -1
- package/build/dist/Utils/StatusPage/GroupTree.js +79 -34
- package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -1
- package/build/dist/Utils/StatusPage/ResourceUptime.js +22 -1
- package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
- package/build/dist/Utils/Telemetry/SavedViewTimeRange.js +79 -0
- package/build/dist/Utils/Telemetry/SavedViewTimeRange.js.map +1 -0
- package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js +236 -0
- package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import SnmpOid from "../../../../Types/Monitor/SnmpMonitor/SnmpOid";
|
|
2
|
+
import SnmpVendorTemplateUtil, {
|
|
3
|
+
SnmpVendorTemplate,
|
|
4
|
+
SnmpVendorTemplates,
|
|
5
|
+
} from "../../../../Types/Monitor/SnmpMonitor/SnmpVendorTemplate";
|
|
6
|
+
|
|
7
|
+
describe("SnmpVendorTemplateUtil", () => {
|
|
8
|
+
describe("getAll", () => {
|
|
9
|
+
test("returns every registered template", () => {
|
|
10
|
+
expect(SnmpVendorTemplateUtil.getAll()).toBe(SnmpVendorTemplates);
|
|
11
|
+
expect(SnmpVendorTemplateUtil.getAll().length).toBeGreaterThan(0);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("every template has an id, label, description and at least one oid", () => {
|
|
15
|
+
const seenIds: Set<string> = new Set<string>();
|
|
16
|
+
|
|
17
|
+
for (const template of SnmpVendorTemplateUtil.getAll()) {
|
|
18
|
+
expect(template.id.length).toBeGreaterThan(0);
|
|
19
|
+
expect(template.label.length).toBeGreaterThan(0);
|
|
20
|
+
expect(template.description.length).toBeGreaterThan(0);
|
|
21
|
+
expect(template.oids.length).toBeGreaterThan(0);
|
|
22
|
+
|
|
23
|
+
// Ids are unique so getById is unambiguous.
|
|
24
|
+
expect(seenIds.has(template.id)).toBe(false);
|
|
25
|
+
seenIds.add(template.id);
|
|
26
|
+
|
|
27
|
+
// Each oid within a template carries an OID string.
|
|
28
|
+
for (const oid of template.oids) {
|
|
29
|
+
expect(oid.oid.length).toBeGreaterThan(0);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe("getById", () => {
|
|
36
|
+
test("returns the matching template", () => {
|
|
37
|
+
const first: SnmpVendorTemplate = SnmpVendorTemplateUtil.getAll()[0]!;
|
|
38
|
+
expect(SnmpVendorTemplateUtil.getById(first.id)).toBe(first);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("returns undefined for an unknown id", () => {
|
|
42
|
+
expect(SnmpVendorTemplateUtil.getById("does-not-exist")).toBeUndefined();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("mergeOids", () => {
|
|
47
|
+
test("returns the existing list unchanged for an unknown template id", () => {
|
|
48
|
+
const existing: Array<SnmpOid> = [{ oid: "1.2.3", name: "custom" }];
|
|
49
|
+
|
|
50
|
+
const merged: Array<SnmpOid> = SnmpVendorTemplateUtil.mergeOids(
|
|
51
|
+
existing,
|
|
52
|
+
"does-not-exist",
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
expect(merged).toBe(existing);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("appends all of a template's oids onto an empty list", () => {
|
|
59
|
+
const template: SnmpVendorTemplate = SnmpVendorTemplateUtil.getAll()[0]!;
|
|
60
|
+
|
|
61
|
+
const merged: Array<SnmpOid> = SnmpVendorTemplateUtil.mergeOids(
|
|
62
|
+
[],
|
|
63
|
+
template.id,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
expect(
|
|
67
|
+
merged.map((o: SnmpOid) => {
|
|
68
|
+
return o.oid;
|
|
69
|
+
}),
|
|
70
|
+
).toEqual(
|
|
71
|
+
template.oids.map((o: SnmpOid) => {
|
|
72
|
+
return o.oid;
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("does not duplicate an oid that already exists", () => {
|
|
78
|
+
const template: SnmpVendorTemplate = SnmpVendorTemplateUtil.getAll()[0]!;
|
|
79
|
+
const firstOid: SnmpOid = template.oids[0]!;
|
|
80
|
+
|
|
81
|
+
const merged: Array<SnmpOid> = SnmpVendorTemplateUtil.mergeOids(
|
|
82
|
+
[{ oid: firstOid.oid, name: "already here" }],
|
|
83
|
+
template.id,
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const occurrences: number = merged.filter((o: SnmpOid) => {
|
|
87
|
+
return o.oid === firstOid.oid;
|
|
88
|
+
}).length;
|
|
89
|
+
expect(occurrences).toBe(1);
|
|
90
|
+
|
|
91
|
+
// The pre-existing entry is preserved (not overwritten by the template's).
|
|
92
|
+
expect(merged[0]).toEqual({ oid: firstOid.oid, name: "already here" });
|
|
93
|
+
|
|
94
|
+
// The merged list has one entry per distinct oid in existing ∪ template.
|
|
95
|
+
expect(merged.length).toBe(template.oids.length);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("is idempotent — applying the same template twice adds nothing new", () => {
|
|
99
|
+
const template: SnmpVendorTemplate = SnmpVendorTemplateUtil.getAll()[0]!;
|
|
100
|
+
|
|
101
|
+
const once: Array<SnmpOid> = SnmpVendorTemplateUtil.mergeOids(
|
|
102
|
+
[],
|
|
103
|
+
template.id,
|
|
104
|
+
);
|
|
105
|
+
const twice: Array<SnmpOid> = SnmpVendorTemplateUtil.mergeOids(
|
|
106
|
+
once,
|
|
107
|
+
template.id,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
expect(
|
|
111
|
+
twice.map((o: SnmpOid) => {
|
|
112
|
+
return o.oid;
|
|
113
|
+
}),
|
|
114
|
+
).toEqual(
|
|
115
|
+
once.map((o: SnmpOid) => {
|
|
116
|
+
return o.oid;
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("preserves the caller's existing oids and appends only new ones", () => {
|
|
122
|
+
const template: SnmpVendorTemplate = SnmpVendorTemplateUtil.getAll()[0]!;
|
|
123
|
+
const custom: SnmpOid = { oid: "9.9.9.9", name: "custom metric" };
|
|
124
|
+
|
|
125
|
+
const merged: Array<SnmpOid> = SnmpVendorTemplateUtil.mergeOids(
|
|
126
|
+
[custom],
|
|
127
|
+
template.id,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// Existing custom oid stays first, template oids follow.
|
|
131
|
+
expect(merged[0]).toEqual(custom);
|
|
132
|
+
expect(merged.length).toBe(1 + template.oids.length);
|
|
133
|
+
for (const oid of template.oids) {
|
|
134
|
+
expect(
|
|
135
|
+
merged.map((o: SnmpOid) => {
|
|
136
|
+
return o.oid;
|
|
137
|
+
}),
|
|
138
|
+
).toContain(oid.oid);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("does not mutate the input array", () => {
|
|
143
|
+
const template: SnmpVendorTemplate = SnmpVendorTemplateUtil.getAll()[0]!;
|
|
144
|
+
const existing: Array<SnmpOid> = [{ oid: "9.9.9.9" }];
|
|
145
|
+
|
|
146
|
+
SnmpVendorTemplateUtil.mergeOids(existing, template.id);
|
|
147
|
+
|
|
148
|
+
expect(existing).toEqual([{ oid: "9.9.9.9" }]);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import ObjectID from "../../Types/ObjectID";
|
|
2
2
|
import Permission, {
|
|
3
3
|
PermissionHelper,
|
|
4
|
+
PermissionProps,
|
|
4
5
|
UserPermission,
|
|
5
6
|
} from "../../Types/Permission";
|
|
6
7
|
import PermissionScope from "../../Types/Database/AccessControl/PermissionScope";
|
|
@@ -15,6 +16,142 @@ describe("Permission", () => {
|
|
|
15
16
|
});
|
|
16
17
|
});
|
|
17
18
|
|
|
19
|
+
/*
|
|
20
|
+
* Integrity of the permission catalogue itself.
|
|
21
|
+
*
|
|
22
|
+
* Both invariants below were violated in this file for a long time and neither
|
|
23
|
+
* failure was visible from the outside:
|
|
24
|
+
*
|
|
25
|
+
* - CreateScheduledMaintenanceTemplateOwnerUser and ...OwnerTeam were declared
|
|
26
|
+
* with the string values of their non-template counterparts, so two distinct
|
|
27
|
+
* capabilities were one permission wearing two names. Granting either
|
|
28
|
+
* conferred both, and owner rows feed the Owned permission scope, so it
|
|
29
|
+
* widened access to scheduled maintenance events the holder did not own.
|
|
30
|
+
*
|
|
31
|
+
* - Sixteen permissions had their PermissionProps object listed twice, so the
|
|
32
|
+
* dashboard's granular picker offered each of them twice.
|
|
33
|
+
*
|
|
34
|
+
* A duplicate is silent by construction: TypeScript is happy, every lookup
|
|
35
|
+
* still resolves, and the only symptom is a picker entry that does not do what
|
|
36
|
+
* its label says. These tests are the tripwire.
|
|
37
|
+
*/
|
|
38
|
+
describe("Permission catalogue integrity", () => {
|
|
39
|
+
test("no two enum members share a string value", () => {
|
|
40
|
+
const seen: Map<string, string> = new Map();
|
|
41
|
+
const collisions: Array<string> = [];
|
|
42
|
+
|
|
43
|
+
for (const [name, value] of Object.entries(Permission)) {
|
|
44
|
+
const previous: string | undefined = seen.get(value);
|
|
45
|
+
|
|
46
|
+
if (previous) {
|
|
47
|
+
collisions.push(`${previous} and ${name} both = "${value}"`);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
seen.set(value, name);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
expect(collisions).toEqual([]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("getAllPermissionProps has no duplicate permission entries", () => {
|
|
58
|
+
const counts: Map<Permission, number> = new Map();
|
|
59
|
+
|
|
60
|
+
for (const props of PermissionHelper.getAllPermissionProps()) {
|
|
61
|
+
counts.set(props.permission, (counts.get(props.permission) || 0) + 1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const duplicates: Array<string> = [];
|
|
65
|
+
for (const [permission, count] of counts) {
|
|
66
|
+
if (count > 1) {
|
|
67
|
+
duplicates.push(`${permission} x${count}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
expect(duplicates).toEqual([]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("every props entry round-trips through getTitle and getDescription", () => {
|
|
75
|
+
/*
|
|
76
|
+
* getTitle/getDescription take the FIRST match while
|
|
77
|
+
* getAllPermissionPropsAsDictionary is last-wins. With duplicates present
|
|
78
|
+
* those two disagree, and the dashboard and the API reference then show
|
|
79
|
+
* different text for the same permission.
|
|
80
|
+
*/
|
|
81
|
+
for (const props of PermissionHelper.getAllPermissionProps()) {
|
|
82
|
+
expect(PermissionHelper.getTitle(props.permission)).toBe(props.title);
|
|
83
|
+
expect(PermissionHelper.getDescription(props.permission)).toBe(
|
|
84
|
+
props.description,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("the props dictionary covers every props entry", () => {
|
|
90
|
+
const dictionary: Record<string, PermissionProps> =
|
|
91
|
+
PermissionHelper.getAllPermissionPropsAsDictionary() as Record<
|
|
92
|
+
string,
|
|
93
|
+
PermissionProps
|
|
94
|
+
>;
|
|
95
|
+
|
|
96
|
+
for (const props of PermissionHelper.getAllPermissionProps()) {
|
|
97
|
+
expect(dictionary[props.permission]).toBeDefined();
|
|
98
|
+
expect(dictionary[props.permission]!.title).toBe(props.title);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("no props entry is keyed to a permission from another resource family", () => {
|
|
103
|
+
/*
|
|
104
|
+
* The template-owner bug was a mis-keyed entry: the title said "Template"
|
|
105
|
+
* while the permission pointed at the non-template member. Catch the shape
|
|
106
|
+
* generically — a title containing "Template" must belong to a permission
|
|
107
|
+
* whose name contains "Template", and vice versa.
|
|
108
|
+
*/
|
|
109
|
+
const mismatched: Array<string> = [];
|
|
110
|
+
|
|
111
|
+
for (const props of PermissionHelper.getAllPermissionProps()) {
|
|
112
|
+
const titleSaysTemplate: boolean = props.title.includes("Template");
|
|
113
|
+
const keySaysTemplate: boolean = props.permission
|
|
114
|
+
.toString()
|
|
115
|
+
.includes("Template");
|
|
116
|
+
|
|
117
|
+
if (titleSaysTemplate !== keySaysTemplate) {
|
|
118
|
+
mismatched.push(`${props.permission} titled "${props.title}"`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
expect(mismatched).toEqual([]);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("every tenant-assignable permission has a non-empty title and description", () => {
|
|
126
|
+
const bad: Array<string> = [];
|
|
127
|
+
|
|
128
|
+
for (const props of PermissionHelper.getTenantPermissionProps()) {
|
|
129
|
+
if (!props.title.trim() || !props.description.trim()) {
|
|
130
|
+
bad.push(props.permission.toString());
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
expect(bad).toEqual([]);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("roles and granular permissions partition the assignable set", () => {
|
|
138
|
+
const assignable: Array<PermissionProps> =
|
|
139
|
+
PermissionHelper.getTenantPermissionProps();
|
|
140
|
+
const roles: Array<PermissionProps> =
|
|
141
|
+
PermissionHelper.getRolePermissionProps();
|
|
142
|
+
|
|
143
|
+
const granular: Array<PermissionProps> = assignable.filter(
|
|
144
|
+
(props: PermissionProps) => {
|
|
145
|
+
return !props.isRolePermission;
|
|
146
|
+
},
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
expect(roles.length + granular.length).toBe(assignable.length);
|
|
150
|
+
expect(roles.length).toBeGreaterThan(10);
|
|
151
|
+
expect(granular.length).toBeGreaterThan(100);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
18
155
|
const makeRow: (
|
|
19
156
|
permission: Permission,
|
|
20
157
|
scope: PermissionScope | undefined,
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import SessionReplayMaskingMode, {
|
|
2
|
+
doesMaskingModeRecordReadableContent,
|
|
3
|
+
parseSessionReplayMaskingMode,
|
|
4
|
+
} from "../../../Types/Rum/SessionReplayMaskingMode";
|
|
5
|
+
import { describe, expect, test } from "@jest/globals";
|
|
6
|
+
|
|
7
|
+
describe("SessionReplayMaskingMode", () => {
|
|
8
|
+
const allModes: Array<SessionReplayMaskingMode> = Object.values(
|
|
9
|
+
SessionReplayMaskingMode,
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
describe("parseSessionReplayMaskingMode", () => {
|
|
13
|
+
test("returns each valid mode when given its own value", () => {
|
|
14
|
+
expect(
|
|
15
|
+
parseSessionReplayMaskingMode(
|
|
16
|
+
SessionReplayMaskingMode.MaskSensitiveInputsOnly,
|
|
17
|
+
),
|
|
18
|
+
).toBe(SessionReplayMaskingMode.MaskSensitiveInputsOnly);
|
|
19
|
+
expect(
|
|
20
|
+
parseSessionReplayMaskingMode(SessionReplayMaskingMode.MaskInputsOnly),
|
|
21
|
+
).toBe(SessionReplayMaskingMode.MaskInputsOnly);
|
|
22
|
+
expect(
|
|
23
|
+
parseSessionReplayMaskingMode(SessionReplayMaskingMode.MaskAllText),
|
|
24
|
+
).toBe(SessionReplayMaskingMode.MaskAllText);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("round-trips every enum member", () => {
|
|
28
|
+
for (const mode of allModes) {
|
|
29
|
+
expect(parseSessionReplayMaskingMode(mode)).toBe(mode);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("fails closed to MaskAllText for unrecognised values", () => {
|
|
34
|
+
const unknownValues: Array<unknown> = [
|
|
35
|
+
undefined,
|
|
36
|
+
null,
|
|
37
|
+
"",
|
|
38
|
+
"maskallText",
|
|
39
|
+
"MaskEverything",
|
|
40
|
+
"mask_inputs_only",
|
|
41
|
+
0,
|
|
42
|
+
1,
|
|
43
|
+
true,
|
|
44
|
+
false,
|
|
45
|
+
{},
|
|
46
|
+
[],
|
|
47
|
+
{ mode: SessionReplayMaskingMode.MaskInputsOnly },
|
|
48
|
+
];
|
|
49
|
+
for (const value of unknownValues) {
|
|
50
|
+
expect(parseSessionReplayMaskingMode(value)).toBe(
|
|
51
|
+
SessionReplayMaskingMode.MaskAllText,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("is case-sensitive and does not coerce near-matches to a relaxed mode", () => {
|
|
57
|
+
// A relaxed-looking-but-wrong string must NOT downgrade privacy.
|
|
58
|
+
expect(parseSessionReplayMaskingMode("maskinputsonly")).toBe(
|
|
59
|
+
SessionReplayMaskingMode.MaskAllText,
|
|
60
|
+
);
|
|
61
|
+
expect(parseSessionReplayMaskingMode("MaskSensitiveInputsOnly ")).toBe(
|
|
62
|
+
SessionReplayMaskingMode.MaskAllText,
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe("doesMaskingModeRecordReadableContent", () => {
|
|
68
|
+
test("is true for both relaxed modes", () => {
|
|
69
|
+
expect(
|
|
70
|
+
doesMaskingModeRecordReadableContent(
|
|
71
|
+
SessionReplayMaskingMode.MaskSensitiveInputsOnly,
|
|
72
|
+
),
|
|
73
|
+
).toBe(true);
|
|
74
|
+
expect(
|
|
75
|
+
doesMaskingModeRecordReadableContent(
|
|
76
|
+
SessionReplayMaskingMode.MaskInputsOnly,
|
|
77
|
+
),
|
|
78
|
+
).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("is false only for the wireframe mode", () => {
|
|
82
|
+
expect(
|
|
83
|
+
doesMaskingModeRecordReadableContent(
|
|
84
|
+
SessionReplayMaskingMode.MaskAllText,
|
|
85
|
+
),
|
|
86
|
+
).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("MaskAllText is the single mode that does not record readable content", () => {
|
|
90
|
+
const recordingModes: Array<SessionReplayMaskingMode> = allModes.filter(
|
|
91
|
+
(mode: SessionReplayMaskingMode) => {
|
|
92
|
+
return doesMaskingModeRecordReadableContent(mode);
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
expect(recordingModes).not.toContain(
|
|
96
|
+
SessionReplayMaskingMode.MaskAllText,
|
|
97
|
+
);
|
|
98
|
+
expect(recordingModes.length).toBe(allModes.length - 1);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -152,17 +152,17 @@ const renderCard: RenderCardFunction = (): void => {
|
|
|
152
152
|
placeholder: "Every severity",
|
|
153
153
|
},
|
|
154
154
|
{
|
|
155
|
-
field: {
|
|
155
|
+
field: { incidentAiMaxConcurrentInvestigations: true },
|
|
156
156
|
stepId: "limits",
|
|
157
|
-
title: "Max Concurrent Investigations",
|
|
157
|
+
title: "Max Concurrent Incident Investigations",
|
|
158
158
|
required: false,
|
|
159
159
|
fieldType: FormFieldSchemaType.Number,
|
|
160
160
|
placeholder: "3",
|
|
161
161
|
},
|
|
162
162
|
{
|
|
163
|
-
field: {
|
|
163
|
+
field: { incidentAiDailyFixTaskLimit: true },
|
|
164
164
|
stepId: "fix-tasks",
|
|
165
|
-
title: "Daily AI Fix Task Limit",
|
|
165
|
+
title: "Daily Incident AI Fix Task Limit",
|
|
166
166
|
required: false,
|
|
167
167
|
fieldType: FormFieldSchemaType.Number,
|
|
168
168
|
placeholder: "25",
|
|
@@ -280,6 +280,6 @@ describe("AI investigation settings card", () => {
|
|
|
280
280
|
* Only the first step's fields are on screen - which is the point of
|
|
281
281
|
* steps, and proves stepId actually took rather than being ignored.
|
|
282
282
|
*/
|
|
283
|
-
expect(screen.queryByText("Daily AI Fix Task Limit")).toBeNull();
|
|
283
|
+
expect(screen.queryByText("Daily Incident AI Fix Task Limit")).toBeNull();
|
|
284
284
|
});
|
|
285
285
|
});
|
|
@@ -283,7 +283,7 @@ describe("CardModelDetail edit modal", () => {
|
|
|
283
283
|
expect(submitted.model.shouldAutoEnableProbeOnNewMonitors).toBe(false);
|
|
284
284
|
});
|
|
285
285
|
|
|
286
|
-
it("
|
|
286
|
+
it("closes the editor without showing a toast after a successful edit", async () => {
|
|
287
287
|
getItemMock.mockResolvedValue(loadedProbe());
|
|
288
288
|
|
|
289
289
|
renderProbeCard();
|
|
@@ -303,13 +303,11 @@ describe("CardModelDetail edit modal", () => {
|
|
|
303
303
|
|
|
304
304
|
await waitFor(
|
|
305
305
|
() => {
|
|
306
|
-
expect(
|
|
306
|
+
expect(screen.queryByText("Save Changes")).toBeNull();
|
|
307
307
|
},
|
|
308
308
|
{ timeout: WAIT_TIMEOUT },
|
|
309
309
|
);
|
|
310
310
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
expect(toast.title).toEqual("Changes saved");
|
|
311
|
+
expect(showToastMock).not.toHaveBeenCalled();
|
|
314
312
|
});
|
|
315
313
|
});
|
|
@@ -6,9 +6,21 @@ import ErrorBoundary, {
|
|
|
6
6
|
isChunkLoadError,
|
|
7
7
|
markChunkLoadErrorReload,
|
|
8
8
|
} from "../../../UI/Components/ErrorBoundary";
|
|
9
|
+
import downloadFile from "../../../UI/Utils/DownloadFile";
|
|
10
|
+
import { ErrorSupportBundle } from "../../../UI/Utils/ErrorSupportBundle";
|
|
9
11
|
import { fireEvent, render, screen } from "@testing-library/react";
|
|
10
12
|
import React, { FunctionComponent, ReactElement, useEffect } from "react";
|
|
11
13
|
|
|
14
|
+
jest.mock("../../../UI/Utils/DownloadFile", () => {
|
|
15
|
+
return {
|
|
16
|
+
__esModule: true,
|
|
17
|
+
default: jest.fn(),
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const downloadFileMock: jest.MockedFunction<typeof downloadFile> =
|
|
22
|
+
downloadFile as jest.MockedFunction<typeof downloadFile>;
|
|
23
|
+
|
|
12
24
|
/*
|
|
13
25
|
* The blank white screen users hit is React unmounting the entire tree when a
|
|
14
26
|
* render throws and nothing catches it. These tests pin down the containment
|
|
@@ -70,6 +82,7 @@ describe("ErrorBoundary", () => {
|
|
|
70
82
|
|
|
71
83
|
beforeEach(() => {
|
|
72
84
|
consoleErrorSpy.mockClear();
|
|
85
|
+
downloadFileMock.mockReset();
|
|
73
86
|
window.sessionStorage.clear();
|
|
74
87
|
mockReload();
|
|
75
88
|
});
|
|
@@ -115,6 +128,18 @@ describe("ErrorBoundary", () => {
|
|
|
115
128
|
|
|
116
129
|
expect(reloadMock).not.toHaveBeenCalled();
|
|
117
130
|
});
|
|
131
|
+
|
|
132
|
+
test("does not show support-bundle controls when the page is healthy", () => {
|
|
133
|
+
render(
|
|
134
|
+
<ErrorBoundary>
|
|
135
|
+
<div>Child Component</div>
|
|
136
|
+
</ErrorBoundary>,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
expect(
|
|
140
|
+
screen.queryByRole("button", { name: "Download support bundle" }),
|
|
141
|
+
).toBeNull();
|
|
142
|
+
});
|
|
118
143
|
});
|
|
119
144
|
|
|
120
145
|
describe("containment", () => {
|
|
@@ -208,6 +233,20 @@ describe("ErrorBoundary", () => {
|
|
|
208
233
|
screen.getByTestId("error-boundary-error-message").textContent,
|
|
209
234
|
).toContain("reading 'queryConfigs'");
|
|
210
235
|
});
|
|
236
|
+
|
|
237
|
+
test("offers a support bundle and explains what it contains", () => {
|
|
238
|
+
render(
|
|
239
|
+
<ErrorBoundary>
|
|
240
|
+
<ThrowOnRender />
|
|
241
|
+
</ErrorBoundary>,
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
expect(
|
|
245
|
+
screen.queryByRole("button", { name: "Download support bundle" }),
|
|
246
|
+
).not.toBeNull();
|
|
247
|
+
expect(screen.queryByText(/scrubbed page URL/)).not.toBeNull();
|
|
248
|
+
expect(screen.queryByText(/never reads cookies/)).not.toBeNull();
|
|
249
|
+
});
|
|
211
250
|
});
|
|
212
251
|
|
|
213
252
|
describe("reporting", () => {
|
|
@@ -222,6 +261,9 @@ describe("ErrorBoundary", () => {
|
|
|
222
261
|
|
|
223
262
|
expect(onError).toHaveBeenCalledTimes(1);
|
|
224
263
|
expect((onError.mock.calls[0]![0] as Error).message).toBe("Boom");
|
|
264
|
+
expect(onError.mock.calls[0]![1].componentStack).toContain(
|
|
265
|
+
"ThrowOnRender",
|
|
266
|
+
);
|
|
225
267
|
});
|
|
226
268
|
|
|
227
269
|
test("logs the error so it still reaches the console and RUM", () => {
|
|
@@ -292,6 +334,122 @@ describe("ErrorBoundary", () => {
|
|
|
292
334
|
expect(reloadMock).toHaveBeenCalledTimes(1);
|
|
293
335
|
});
|
|
294
336
|
|
|
337
|
+
test("downloads the caught error and React component stack", () => {
|
|
338
|
+
render(
|
|
339
|
+
<ErrorBoundary>
|
|
340
|
+
<ThrowOnRender message="Trace chart render exploded" />
|
|
341
|
+
</ErrorBoundary>,
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
fireEvent.click(
|
|
345
|
+
screen.getByTestId("error-boundary-download-support-bundle"),
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
expect(downloadFileMock).toHaveBeenCalledTimes(1);
|
|
349
|
+
expect(reloadMock).not.toHaveBeenCalled();
|
|
350
|
+
expect(screen.queryByTestId("error-boundary-fallback")).not.toBeNull();
|
|
351
|
+
|
|
352
|
+
const download: Parameters<typeof downloadFile>[0] =
|
|
353
|
+
downloadFileMock.mock.calls[0]![0];
|
|
354
|
+
const bundle: ErrorSupportBundle = JSON.parse(
|
|
355
|
+
download.content as string,
|
|
356
|
+
) as ErrorSupportBundle;
|
|
357
|
+
|
|
358
|
+
expect(download.filename).toMatch(
|
|
359
|
+
/^oneuptime-error-support-bundle-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}\.json$/,
|
|
360
|
+
);
|
|
361
|
+
expect(download.mimeType).toBe("application/json;charset=utf-8;");
|
|
362
|
+
expect(bundle.error.message).toBe("Trace chart render exploded");
|
|
363
|
+
expect(bundle.error.stack).toContain("ThrowOnRender");
|
|
364
|
+
expect(bundle.react.component).toBe("ThrowOnRender");
|
|
365
|
+
expect(bundle.react.componentStack).toContain("ThrowOnRender");
|
|
366
|
+
expect(Date.parse(bundle.errorCapturedAt)).toBeLessThanOrEqual(
|
|
367
|
+
Date.parse(bundle.generatedAt),
|
|
368
|
+
);
|
|
369
|
+
expect(
|
|
370
|
+
screen.getByTestId("error-boundary-support-bundle-status").textContent,
|
|
371
|
+
).toContain("Support bundle downloaded");
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test("keeps component diagnostics when a component throws NaN", () => {
|
|
375
|
+
const ThrowNaN: FunctionComponent = (): ReactElement => {
|
|
376
|
+
throw Number.NaN;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
render(
|
|
380
|
+
<ErrorBoundary>
|
|
381
|
+
<ThrowNaN />
|
|
382
|
+
</ErrorBoundary>,
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
fireEvent.click(
|
|
386
|
+
screen.getByTestId("error-boundary-download-support-bundle"),
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
const bundle: ErrorSupportBundle = JSON.parse(
|
|
390
|
+
downloadFileMock.mock.calls[0]![0].content as string,
|
|
391
|
+
) as ErrorSupportBundle;
|
|
392
|
+
|
|
393
|
+
expect(bundle.error.message).toBe("NaN");
|
|
394
|
+
expect(bundle.react.componentStack).toContain("ThrowNaN");
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
test("shows a recoverable message if the browser blocks the download", () => {
|
|
398
|
+
downloadFileMock.mockImplementation(() => {
|
|
399
|
+
throw new Error("Downloads disabled");
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
render(
|
|
403
|
+
<ErrorBoundary>
|
|
404
|
+
<ThrowOnRender />
|
|
405
|
+
</ErrorBoundary>,
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
fireEvent.click(
|
|
409
|
+
screen.getByTestId("error-boundary-download-support-bundle"),
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
expect(
|
|
413
|
+
screen.getByTestId("error-boundary-support-bundle-status").textContent,
|
|
414
|
+
).toContain("could not be downloaded");
|
|
415
|
+
expect(screen.queryByTestId("error-boundary-fallback")).not.toBeNull();
|
|
416
|
+
expect(reloadMock).not.toHaveBeenCalled();
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
test("downloads fresh diagnostics after recovering and crashing again", () => {
|
|
420
|
+
let errorMessage: string = "First render failure";
|
|
421
|
+
|
|
422
|
+
const ThrowsCurrentError: FunctionComponent = (): ReactElement => {
|
|
423
|
+
throw new Error(errorMessage);
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
render(
|
|
427
|
+
<ErrorBoundary>
|
|
428
|
+
<ThrowsCurrentError />
|
|
429
|
+
</ErrorBoundary>,
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
fireEvent.click(
|
|
433
|
+
screen.getByTestId("error-boundary-download-support-bundle"),
|
|
434
|
+
);
|
|
435
|
+
const firstBundle: ErrorSupportBundle = JSON.parse(
|
|
436
|
+
downloadFileMock.mock.calls[0]![0].content as string,
|
|
437
|
+
) as ErrorSupportBundle;
|
|
438
|
+
|
|
439
|
+
errorMessage = "Second render failure";
|
|
440
|
+
fireEvent.click(screen.getByTestId("error-boundary-try-again"));
|
|
441
|
+
fireEvent.click(
|
|
442
|
+
screen.getByTestId("error-boundary-download-support-bundle"),
|
|
443
|
+
);
|
|
444
|
+
const secondBundle: ErrorSupportBundle = JSON.parse(
|
|
445
|
+
downloadFileMock.mock.calls[1]![0].content as string,
|
|
446
|
+
) as ErrorSupportBundle;
|
|
447
|
+
|
|
448
|
+
expect(firstBundle.error.message).toBe("First render failure");
|
|
449
|
+
expect(secondBundle.error.message).toBe("Second render failure");
|
|
450
|
+
expect(secondBundle.react.componentStack).toContain("ThrowsCurrentError");
|
|
451
|
+
});
|
|
452
|
+
|
|
295
453
|
test("changing the resetKey clears the error (navigating away)", () => {
|
|
296
454
|
const { rerender } = render(
|
|
297
455
|
<ErrorBoundary resetKey="/dashboard/monitors/broken">
|