@oneuptime/common 14.0.2 → 14.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/Alert.ts +55 -0
- package/Models/DatabaseModels/AlertFeed.ts +2 -0
- package/Models/DatabaseModels/DatabaseServer.ts +1650 -0
- package/Models/DatabaseModels/DatabaseServerEndpoint.ts +540 -0
- package/Models/DatabaseModels/DatabaseServerFeed.ts +625 -0
- package/Models/DatabaseModels/DatabaseServerLabelRule.ts +514 -0
- package/Models/DatabaseModels/DatabaseServerOwnerRule.ts +596 -0
- package/Models/DatabaseModels/DatabaseServerOwnerTeam.ts +491 -0
- package/Models/DatabaseModels/DatabaseServerOwnerUser.ts +490 -0
- package/Models/DatabaseModels/Incident.ts +55 -0
- package/Models/DatabaseModels/IncidentAlert.ts +540 -0
- package/Models/DatabaseModels/IncidentFeed.ts +2 -0
- package/Models/DatabaseModels/Index.ts +18 -0
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +26 -0
- package/Models/DatabaseModels/Project.ts +60 -0
- package/Models/DatabaseModels/RumApplication.ts +52 -1
- package/Models/DatabaseModels/ScheduledMaintenance.ts +55 -0
- package/Models/DatabaseModels/TelemetryException.ts +2 -0
- package/Models/DatabaseModels/UserProjectSsoConsent.ts +146 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.ts +28 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.ts +89 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.ts +673 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Middleware/MultipartFormData.ts +1 -1
- package/Server/Middleware/TelemetryIngest.ts +15 -8
- package/Server/Services/AlertReminderRuleService.ts +100 -24
- package/Server/Services/DatabaseServerEndpointService.ts +895 -0
- package/Server/Services/DatabaseServerFeedService.ts +128 -0
- package/Server/Services/DatabaseServerLabelRuleEngineService.ts +340 -0
- package/Server/Services/DatabaseServerLabelRuleService.ts +14 -0
- package/Server/Services/DatabaseServerOwnerRuleEngineService.ts +425 -0
- package/Server/Services/DatabaseServerOwnerRuleService.ts +14 -0
- package/Server/Services/DatabaseServerOwnerTeamService.ts +180 -0
- package/Server/Services/DatabaseServerOwnerUserService.ts +173 -0
- package/Server/Services/DatabaseServerService.ts +4546 -0
- package/Server/Services/DatabaseService.ts +14 -8
- package/Server/Services/DockerResourceService.ts +43 -2
- package/Server/Services/IncidentAlertService.ts +1811 -0
- package/Server/Services/IncidentReminderRuleService.ts +103 -23
- package/Server/Services/IncidentService.ts +210 -10
- package/Server/Services/IncidentStateTimelineService.ts +24 -0
- package/Server/Services/Index.ts +18 -0
- package/Server/Services/InventoryItemService.ts +113 -5
- package/Server/Services/IoTDeviceService.ts +145 -37
- package/Server/Services/OpenTelemetryIngestService.ts +19 -0
- package/Server/Services/PodmanResourceService.ts +43 -2
- package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +107 -23
- package/Server/Services/ServiceService.ts +1 -1
- package/Server/Services/TelemetryUsageBillingService.ts +29 -2
- package/Server/Services/UserProjectSsoConsentService.ts +74 -0
- package/Server/Services/UserService.ts +14 -1
- package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +18 -0
- package/Server/Types/Database/QueryHelper.ts +30 -1
- package/Server/Utils/AI/Toolbox/ResourceTools.ts +69 -1
- package/Server/Utils/Database/AffectedResourceRelations.ts +6 -0
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +188 -6
- package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +2262 -755
- package/Server/Utils/Monitor/MonitorIncident.ts +28 -1
- package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +268 -6
- package/Server/Utils/Monitor/MonitorResource.ts +47 -11
- package/Server/Utils/Monitor/MonitorResourceContext.ts +1 -0
- package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +239 -21
- package/Server/Utils/Monitor/PerSeriesResolutionRootCause.ts +43 -0
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +291 -0
- package/Server/Utils/Monitor/SeriesResourceLinker.ts +111 -12
- package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
- package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +24 -2
- package/Server/Utils/StartServer.ts +26 -6
- package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
- package/Server/Utils/Telemetry/DatabaseEndpointDiscovery.ts +688 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +5 -1
- package/Server/Utils/Telemetry/LogExceptionExtractor.ts +18 -1
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +175 -12
- package/Server/Utils/Telemetry/ResourceFacetResolver.ts +12 -0
- package/Server/Utils/Telemetry/ServiceDependencyDiscovery.ts +606 -36
- package/Server/Utils/Telemetry/Telemetry.ts +16 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +22 -22
- package/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.ts +16 -10
- package/Tests/App/Accounts/SignedInRedirect.test.tsx +241 -0
- package/Tests/App/AdminDashboard/AdminHeaderSmallScreens.test.tsx +221 -4
- package/Tests/App/Dashboard/AIInsightsResourcePageContext.test.ts +42 -0
- package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +100 -0
- package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +86 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +2 -0
- package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +19 -0
- package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +14 -0
- package/Tests/App/Dashboard/BulkIncidentLinkActions.test.tsx +869 -0
- package/Tests/App/Dashboard/CephMetricTooltips.test.tsx +1777 -0
- package/Tests/App/Dashboard/CloudFleetSummary.test.ts +8 -0
- package/Tests/App/Dashboard/CloudMetricTooltips.test.tsx +887 -0
- package/Tests/App/Dashboard/ContainerHostMetricTooltips.test.tsx +1309 -0
- package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +26 -1
- package/Tests/App/Dashboard/DashboardAppSsoError.test.tsx +501 -0
- package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +64 -1
- package/Tests/App/Dashboard/DashboardHeaderSmallScreens.test.tsx +178 -18
- package/Tests/App/Dashboard/DashboardMasterPageSsoRedirect.test.tsx +444 -0
- package/Tests/App/Dashboard/DashboardNavBarForeignHiddenRule.test.tsx +573 -0
- package/Tests/App/Dashboard/DatabaseChartAxes.test.tsx +431 -0
- package/Tests/App/Dashboard/DatabaseContainerDetailBadge.test.tsx +249 -0
- package/Tests/App/Dashboard/DatabaseDashboardE2EFixes.test.ts +681 -0
- package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +1801 -0
- package/Tests/App/Dashboard/DatabaseFleetSummaryRefresh.test.tsx +280 -0
- package/Tests/App/Dashboard/DatabaseFormHelpers.test.tsx +321 -0
- package/Tests/App/Dashboard/DatabaseMetricMonitorLink.test.tsx +795 -0
- package/Tests/App/Dashboard/DatabaseOverviewHeader.test.tsx +580 -0
- package/Tests/App/Dashboard/DatabaseRecommendationsEngineMetrics.test.ts +270 -0
- package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +1486 -0
- package/Tests/App/Dashboard/DatabaseServerEngineMetricsFilter.test.ts +223 -0
- package/Tests/App/Dashboard/DatabaseServerPresentation.test.ts +435 -0
- package/Tests/App/Dashboard/DatabaseServerSummary.test.ts +186 -0
- package/Tests/App/Dashboard/DatabaseServerTelemetryQueries.test.ts +1553 -0
- package/Tests/App/Dashboard/DatabaseTableTruncation.test.tsx +326 -0
- package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +512 -0
- package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +774 -0
- package/Tests/App/Dashboard/DatabaseViewLayout.test.tsx +233 -0
- package/Tests/App/Dashboard/DatabaseWorkloadBadge.test.tsx +972 -0
- package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +5 -0
- package/Tests/App/Dashboard/DiscoveryReviewUnnamedHostHint.test.tsx +1061 -0
- package/Tests/App/Dashboard/DiscoveryScanLivePage.test.tsx +39 -0
- package/Tests/App/Dashboard/DockerSwarmClustersListTooltips.test.tsx +414 -0
- package/Tests/App/Dashboard/DockerSwarmMetricTooltips.test.ts +796 -0
- package/Tests/App/Dashboard/DockerSwarmOverviewTooltips.test.tsx +747 -0
- package/Tests/App/Dashboard/EventOverlayScope.test.ts +147 -0
- package/Tests/App/Dashboard/EventOverlayScopeHook.test.tsx +1 -0
- package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +38 -18
- package/Tests/App/Dashboard/HostDetailViewTooltips.test.tsx +725 -0
- package/Tests/App/Dashboard/HostListColumnTooltips.test.tsx +472 -0
- package/Tests/App/Dashboard/HostMetricTooltips.test.ts +709 -0
- package/Tests/App/Dashboard/HostOverviewTooltips.test.tsx +698 -0
- package/Tests/App/Dashboard/HostTooltipHarness.ts +124 -0
- package/Tests/App/Dashboard/HostsListTooltips.test.tsx +395 -0
- package/Tests/App/Dashboard/IncidentAlertLinkDialog.test.tsx +691 -0
- package/Tests/App/Dashboard/IncidentAlertLinkHelpers.test.ts +458 -0
- package/Tests/App/Dashboard/IncidentAlertLinkPages.test.tsx +828 -0
- package/Tests/App/Dashboard/IncidentAlertLinkSideMenus.test.tsx +231 -0
- package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +689 -0
- package/Tests/App/Dashboard/IoTMetricTooltips.test.tsx +1123 -0
- package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +837 -0
- package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +699 -0
- package/Tests/App/Dashboard/KubernetesContainersTabTooltips.test.tsx +320 -0
- package/Tests/App/Dashboard/KubernetesCostTooltips.test.tsx +502 -0
- package/Tests/App/Dashboard/KubernetesInsightsTooltips.test.tsx +222 -0
- package/Tests/App/Dashboard/KubernetesResourceMetricTooltips.test.ts +561 -0
- package/Tests/App/Dashboard/KubernetesResourcePagesTooltips.test.tsx +1168 -0
- package/Tests/App/Dashboard/LinkIncidentAlertModal.test.tsx +154 -0
- package/Tests/App/Dashboard/LogsResourceFacetChips.test.tsx +1 -0
- package/Tests/App/Dashboard/MetricDescriptionRules.ts +170 -0
- package/Tests/App/Dashboard/MetricDescriptionsCatalog.test.ts +206 -0
- package/Tests/App/Dashboard/MetricDescriptionsConsistency.test.ts +275 -0
- package/Tests/App/Dashboard/MetricsViewerDrillDown.test.tsx +310 -0
- package/Tests/App/Dashboard/MetricsViewerEntityScopedRows.test.tsx +495 -0
- package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +132 -1
- package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +5 -0
- package/Tests/App/Dashboard/NetworkDeviceTooltips.test.tsx +852 -0
- package/Tests/App/Dashboard/NetworkDiagnosticsTooltips.test.tsx +556 -0
- package/Tests/App/Dashboard/NetworkMetricTooltips.test.ts +1348 -0
- package/Tests/App/Dashboard/NetworkSiteTooltips.test.tsx +679 -0
- package/Tests/App/Dashboard/ProductOrientation.test.tsx +4 -3
- package/Tests/App/Dashboard/ProxmoxMetricTooltips.test.ts +592 -0
- package/Tests/App/Dashboard/ProxmoxOverviewTooltips.test.tsx +1002 -0
- package/Tests/App/Dashboard/ProxmoxResourcePageTooltips.test.tsx +923 -0
- package/Tests/App/Dashboard/RecommendationsLoadedContext.test.tsx +349 -0
- package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +62 -0
- package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +8 -1
- package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +14 -0
- package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +1066 -0
- package/Tests/App/Dashboard/RumOverviewPage.test.tsx +1195 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +2 -0
- package/Tests/App/Dashboard/ServerlessMetricTooltips.test.tsx +474 -0
- package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +1085 -0
- package/Tests/App/Dashboard/ServiceMetricTooltips.test.tsx +772 -0
- package/Tests/App/Dashboard/SessionReplayAuditTable.test.tsx +56 -1
- package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +1207 -0
- package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +10 -0
- package/Tests/App/Dashboard/TelemetryResourceTabTooltips.test.tsx +437 -0
- package/Tests/App/Dashboard/TracesResourceFacets.test.tsx +2 -1
- package/Tests/App/Dashboard/VMwareMetricTooltips.test.ts +801 -0
- package/Tests/App/Dashboard/VMwareOverviewTooltips.test.tsx +601 -0
- package/Tests/App/Dashboard/VMwareResourcePagesTooltips.test.tsx +646 -0
- package/Tests/App/Dashboard/VMwareTooltipHarness.ts +145 -0
- package/Tests/App/StatusPage/StatusPageHeaderForeignHiddenRule.test.tsx +284 -0
- package/Tests/ClassStrings.ts +178 -0
- package/Tests/Fixtures/MobilePasskeyUser.json +1 -2
- package/Tests/ForeignHiddenRuleGuard.ts +2152 -0
- package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +2250 -0
- package/Tests/Models/DatabaseModels/IncidentAlertModel.test.ts +554 -0
- package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +24 -5
- package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +117 -0
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
- package/Tests/RenderedMarkup.ts +518 -0
- package/Tests/ResponsiveVisibility.test.ts +490 -4
- package/Tests/ResponsiveVisibility.ts +505 -52
- package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
- package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +541 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentAlertMigration.test.ts +341 -0
- package/Tests/Server/Infrastructure/Postgres/AddSessionReplaySameOriginTracePropagationMigration.test.ts +247 -0
- package/Tests/Server/Infrastructure/Postgres/AddUserProjectSsoConsentMigration.test.ts +234 -0
- package/Tests/Server/Services/ContainerSnapshotUpsertPostgres.test.ts +319 -0
- package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +13 -0
- package/Tests/Server/Services/DatabaseServerEndpointService.test.ts +2033 -0
- package/Tests/Server/Services/DatabaseServerFeedAndOwnerServices.test.ts +529 -0
- package/Tests/Server/Services/DatabaseServerLifecycle.test.ts +3302 -0
- package/Tests/Server/Services/DatabaseServerRegistries.test.ts +603 -0
- package/Tests/Server/Services/DatabaseServerRuleEngines.test.ts +754 -0
- package/Tests/Server/Services/DatabaseServerService.test.ts +3558 -0
- package/Tests/Server/Services/DatabaseServerSqlPostgres.test.ts +2045 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedPostgres.test.ts +362 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedReply.test.ts +210 -0
- package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +18 -0
- package/Tests/Server/Services/IncidentAlertPostgres.test.ts +903 -0
- package/Tests/Server/Services/IncidentAlertRegistration.test.ts +96 -0
- package/Tests/Server/Services/IncidentAlertService.test.ts +2225 -0
- package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +2 -0
- package/Tests/Server/Services/IncidentAlertStateCascade.test.ts +970 -0
- package/Tests/Server/Services/IncidentCreateFromAlerts.test.ts +1284 -0
- package/Tests/Server/Services/IncomingCallPolicyEscalationRuleTarget.test.ts +225 -0
- package/Tests/Server/Services/InventoryItemDisplayName.test.ts +280 -2
- package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +8 -2
- package/Tests/Server/Services/IoTDeviceLatestMetricsPostgres.test.ts +689 -0
- package/Tests/Server/Services/IoTDeviceService.test.ts +379 -0
- package/Tests/Server/Services/LatestMetricsValuesShape.test.ts +360 -0
- package/Tests/Server/Services/LlmModelPriceService.test.ts +402 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +7 -1
- package/Tests/Server/Services/OwnerRowUniqueness.test.ts +61 -9
- package/Tests/Server/Services/ReminderRuleLookupPostgres.test.ts +2250 -0
- package/Tests/Server/Services/ReminderRuleRefreshOpenSubjectsPaging.test.ts +653 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +68 -20
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
- package/Tests/Server/Services/TelemetryResourceFacetCounting.test.ts +9 -0
- package/Tests/Server/Services/UserProjectSsoConsentPostgres.test.ts +153 -0
- package/Tests/Server/Services/UserProjectSsoConsentService.test.ts +192 -0
- package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +71 -0
- package/Tests/Server/Services/UserSignupIdTakeoverPostgres.test.ts +238 -0
- package/Tests/Server/Services/WorkspaceNotificationSummaryHelpers.test.ts +592 -0
- package/Tests/Server/Types/Billing/PayAsYouGoMeteredAuthorization.test.ts +3 -0
- package/Tests/Server/Types/Database/EventOverlayScopePostgres.test.ts +1 -0
- package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +11 -0
- package/Tests/Server/Types/Database/Permissions/InfrastructureOwnershipScope.test.ts +8 -0
- package/Tests/Server/Types/Database/QueryHelperBooleanForCriteriaBackedRule.test.ts +1094 -0
- package/Tests/Server/Utils/AI/ResourceTools.test.ts +134 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +41 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +279 -9
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaDisplayContext.test.ts +933 -0
- package/Tests/Server/Utils/Monitor/DatabaseMaintenanceSuppression.test.ts +582 -0
- package/Tests/Server/Utils/Monitor/DatabaseServerMonitorLinking.test.ts +753 -0
- package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +119 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +60 -25
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorAffectedResourceEdgeCases.test.ts +1719 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesAffectedResources.test.ts +1447 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesRatioEmail.test.ts +352 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformMetricUnits.test.ts +717 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformRawScan.test.ts +2125 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformSeriesAffectedResources.test.ts +1288 -0
- package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +88 -0
- package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +205 -0
- package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +62 -0
- package/Tests/Server/Utils/Monitor/MonitorResourceDatabaseMaintenance.test.ts +803 -0
- package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +133 -3
- package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/PerSeriesRecoveryResolution.test.ts +472 -1
- package/Tests/Server/Utils/Monitor/PerSeriesSecondEntityAlerting.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +236 -2
- package/Tests/Server/Utils/SeriesResourceLabels.test.ts +70 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCachePolicy.test.ts +103 -0
- package/Tests/Server/Utils/StartServerProtobufContentType.test.ts +54 -0
- package/Tests/Server/Utils/Telemetry/DatabaseEndpointDiscovery.test.ts +1895 -0
- package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +7 -0
- package/Tests/Server/Utils/Telemetry/LogExceptionExtractorSeverityGate.test.ts +90 -0
- package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +570 -0
- package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +97 -2
- package/Tests/Server/Utils/Telemetry/ServiceDependencyDiscovery.test.ts +1337 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +295 -0
- package/Tests/Types/AI/AIResourceContext.test.ts +30 -0
- package/Tests/Types/DatabaseServer/DatabaseContainerClassifier.test.ts +3644 -0
- package/Tests/Types/DatabaseServer/DatabaseContainerCommand.test.ts +340 -0
- package/Tests/Types/DatabaseServer/DatabaseContainerCommandCorpus.ts +826 -0
- package/Tests/Types/DatabaseServer/DatabaseContainerCommandPostgres.test.ts +150 -0
- package/Tests/Types/DatabaseServer/DatabaseEndpoint.test.ts +2657 -0
- package/Tests/Types/DatabaseServer/DatabaseServerDiscoverySource.test.ts +64 -0
- package/Tests/Types/DatabaseServer/DatabaseServerEntityKeys.test.ts +952 -0
- package/Tests/Types/DatabaseServer/DatabaseServerMetricCatalog.test.ts +1161 -0
- package/Tests/Types/DatabaseServer/DatabaseSystem.test.ts +1514 -0
- package/Tests/Types/DatabaseServer/DatabaseTelemetryResolver.test.ts +1558 -0
- package/Tests/Types/Monitor/CephAlertTemplates.test.ts +255 -6
- package/Tests/Types/Monitor/DatabaseAlertTemplates.test.ts +2254 -0
- package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +298 -0
- package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +271 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +253 -9
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +13 -5
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +4 -2
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +111 -2
- package/Tests/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.test.ts +61 -0
- package/Tests/Types/Monitor/SeriesContext/SeriesLabelDisplay.test.ts +114 -0
- package/Tests/Types/Monitor/Utils/KubernetesAgentEmittedMetrics.ts +181 -0
- package/Tests/Types/NetworkDevice/DiscoveredHostNamingStatus.test.ts +401 -0
- package/Tests/Types/Rum/WebVitals.test.ts +173 -0
- package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +4 -1
- package/Tests/Types/Telemetry/ResourceFacetCatalog.test.ts +24 -2
- package/Tests/UI/Components/Card.test.tsx +93 -2
- package/Tests/UI/Components/FeedOptions.test.ts +6 -0
- package/Tests/UI/Components/FeedOptionsButton.test.tsx +8 -1
- package/Tests/UI/Components/FieldLabelInfoCardTooltip.test.tsx +413 -0
- package/Tests/UI/Components/ForeignHiddenRuleResilience.test.tsx +1147 -0
- package/Tests/UI/Components/HeaderRightRail.test.tsx +41 -1
- package/Tests/UI/Components/InfoTooltip.test.tsx +242 -0
- package/Tests/UI/Components/LogsEntityNames.test.ts +16 -1
- package/Tests/UI/Components/LogsFacetSidebarHiddenFacets.test.tsx +7 -3
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +207 -3
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDeleteVerb.test.tsx +367 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableCreateButtonTitle.test.tsx +275 -0
- package/Tests/UI/Components/ResourceFacetConfigs.test.ts +2 -1
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- package/Tests/UI/Components/SrOnlyForeignRule.test.tsx +994 -0
- package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +34 -1
- package/Tests/UI/Components/TableHeaderTooltip.test.tsx +662 -0
- package/Tests/UI/Components/TelemetryFacetSidebarHiddenFacets.test.tsx +4 -1
- package/Tests/UI/ForeignHiddenRuleGuard.test.ts +1951 -0
- package/Tests/UI/Rum/PrivacySummaryCard.test.tsx +166 -9
- package/Tests/UI/Rum/RecordingHealthTooltips.test.tsx +447 -0
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +164 -0
- package/Tests/UI/Rum/ReplayFrameCapture.test.ts +6750 -0
- package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +19 -1
- package/Tests/UI/Rum/ReplayRail.test.tsx +723 -12
- package/Tests/UI/Rum/ReplayRailDetail.test.tsx +86 -0
- package/Tests/UI/Rum/ReplayScreenshot.test.ts +2124 -0
- package/Tests/UI/Rum/ReplayScreenshotActions.test.tsx +3288 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +62 -0
- package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +983 -2
- package/Tests/UI/Rum/ReplayTabSwitcher.test.tsx +19 -1
- package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +149 -0
- package/Tests/UI/Rum/SessionReplaySetupGuide.test.tsx +107 -6
- package/Tests/UI/Rum/SessionReplayUsersTableTooltips.test.tsx +330 -0
- package/Tests/UI/Rum/UserFlowTooltips.test.tsx +886 -0
- package/Tests/UI/TailwindPlayCdnForeignHiddenRule.test.ts +1005 -0
- package/Tests/UI/ThemeGroupHoverReveal.test.ts +847 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +80 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +23 -0
- package/Tests/UI/Utils/ModelAPISsoRedirect.test.ts +127 -0
- package/Tests/UI/Utils/Navigation.test.ts +102 -0
- package/Tests/UI/Utils/PermissionGate.test.ts +76 -0
- package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +23 -0
- package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +696 -0
- package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
- package/Tests/Utils/MetricUnitUtil.test.ts +227 -2
- package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +288 -2
- package/Tests/Utils/Monitor/PlatformResourceIdentityAndUnits.test.ts +1559 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.test.ts +2546 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +315 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +336 -1
- package/Tests/Utils/NetworkDiscovery/NetbiosNameUtil.test.ts +244 -0
- package/Tests/Utils/Rum/SessionTraceState.test.ts +239 -0
- package/Tests/Utils/ValueFormatter.test.ts +50 -0
- package/Tests/tsconfig.json +16 -0
- package/Types/AI/AIResourceContext.ts +7 -0
- package/Types/Dashboard/DashboardTemplates.ts +2 -1
- package/Types/DatabaseServer/DatabaseConnectionSpan.ts +77 -0
- package/Types/DatabaseServer/DatabaseContainerClassifier.ts +2068 -0
- package/Types/DatabaseServer/DatabaseContainerCommand.ts +801 -0
- package/Types/DatabaseServer/DatabaseEndpoint.ts +1943 -0
- package/Types/DatabaseServer/DatabaseServerDiscoverySource.ts +68 -0
- package/Types/DatabaseServer/DatabaseServerMetricCatalog.ts +966 -0
- package/Types/DatabaseServer/DatabaseSystem.ts +2124 -0
- package/Types/DatabaseServer/DatabaseTelemetryResolver.ts +555 -0
- package/Types/Email/EmailTemplateType.ts +1 -0
- package/Types/Incident/IncidentAlertLink.ts +38 -0
- package/Types/Monitor/CephAlertTemplates.ts +18 -0
- package/Types/Monitor/DatabaseAlertTemplates.ts +2020 -0
- package/Types/Monitor/KubernetesAlertTemplates.ts +64 -24
- package/Types/Monitor/KubernetesMetricCatalog.ts +118 -19
- package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +14 -0
- package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +93 -10
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +109 -8
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +36 -7
- package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +43 -2
- package/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.ts +41 -6
- package/Types/Monitor/SeriesContext/SeriesLabelDisplay.ts +26 -0
- package/Types/NetworkDevice/DiscoveredHostNamingStatus.ts +147 -0
- package/Types/Permission.ts +315 -0
- package/Types/Probe/ProbeApiIngestResponse.ts +8 -0
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +10 -0
- package/Types/Rules/RuleRun.ts +12 -0
- package/Types/Rum/SessionReplay.ts +25 -6
- package/Types/Rum/WebVitals.ts +52 -0
- package/Types/Telemetry/EntityType.ts +23 -0
- package/Types/Telemetry/ResourceFacetCatalog.ts +14 -0
- package/Types/Telemetry/ServiceType.ts +8 -0
- package/UI/Components/Banner/Banner.tsx +1 -1
- package/UI/Components/Card/Card.tsx +1 -1
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +2 -2
- package/UI/Components/Charts/Line/LineChart.tsx +7 -0
- package/UI/Components/CommandPalette/CommandPalette.tsx +1 -1
- package/UI/Components/Date/StartAndEndDate.tsx +5 -5
- package/UI/Components/Detail/FieldLabel.tsx +8 -0
- package/UI/Components/Diagram/HorizontalStepChain.tsx +1 -1
- package/UI/Components/Feed/FeedOptionsButton.tsx +1 -1
- package/UI/Components/Forms/BasicForm.tsx +1 -1
- package/UI/Components/Header/Header.tsx +1 -1
- package/UI/Components/Header/HeaderIconDropdownButton.tsx +1 -1
- package/UI/Components/HeaderAlert/HeaderAlert.tsx +1 -1
- package/UI/Components/InfoCard/InfoCard.tsx +47 -1
- package/UI/Components/LogsViewer/components/FacetValueRow.tsx +1 -1
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +1 -1
- package/UI/Components/Modal/ModalFooter.tsx +1 -1
- package/UI/Components/ModelDetail/CardModelDetail.tsx +2 -2
- package/UI/Components/ModelTable/BaseModelTable.tsx +99 -21
- package/UI/Components/ModelTable/Column.ts +5 -0
- package/UI/Components/Navbar/NavBar.tsx +9 -2
- package/UI/Components/Navbar/NavBarMenuModal.tsx +2 -2
- package/UI/Components/Page/Page.tsx +1 -1
- package/UI/Components/Pagination/Pagination.tsx +3 -3
- package/UI/Components/ProgressButtons/ProgressButtonItem.tsx +1 -1
- package/UI/Components/SideMenu/SideMenu.tsx +4 -1
- package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +4 -2
- package/UI/Components/StatusPage/ResourceList.tsx +6 -6
- package/UI/Components/Table/TableHeader.tsx +47 -2
- package/UI/Components/Table/Types/Column.ts +7 -0
- package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +1 -1
- package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +1 -1
- package/UI/Components/Tooltip/InfoTooltip.tsx +78 -0
- package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +1 -1
- package/UI/Components/Workflow/ComponentsModal.tsx +2 -2
- package/UI/Components/Workflow/StepTraceViewer.tsx +1 -1
- package/UI/Styles/Theme.css +8 -1
- package/UI/Utils/Navigation.ts +89 -42
- package/UI/Utils/PermissionGate.ts +11 -2
- package/UI/Utils/Telemetry/TelemetryEntityNames.ts +7 -0
- package/UI/Utils/TelemetryService.ts +4 -3
- package/Utils/Incident/IncidentFromAlerts.ts +516 -0
- package/Utils/LabelRuleImportExport.ts +2 -0
- package/Utils/MetricUnitUtil.ts +103 -11
- package/Utils/Monitor/MetricValueFormatter.ts +66 -13
- package/Utils/Monitor/PlatformMetricUnitUtil.ts +215 -0
- package/Utils/Monitor/PlatformResourceIdentity.ts +514 -0
- package/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.ts +425 -0
- package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +32 -0
- package/Utils/NetworkDiscovery/NetbiosNameUtil.ts +73 -0
- package/Utils/Rum/SessionTraceState.ts +212 -0
- package/Utils/Telemetry/DatabaseServerEntityKeys.ts +275 -0
- package/Utils/Telemetry/EntityKey.ts +176 -0
- package/Utils/ValueFormatter.ts +16 -0
- package/build/dist/Models/DatabaseModels/Alert.js +54 -0
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseServer.js +1683 -0
- package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js +559 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js +648 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js +522 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js +603 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js +506 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js +505 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Incident.js +54 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentAlert.js +553 -0
- package/build/dist/Models/DatabaseModels/IncidentAlert.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +18 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +62 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +53 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +54 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryException.js +2 -0
- package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js +162 -0
- package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js +42 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js +240 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/MultipartFormData.js +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +15 -8
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AlertReminderRuleService.js +72 -23
- package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseServerEndpointService.js +694 -0
- package/build/dist/Server/Services/DatabaseServerEndpointService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerFeedService.js +110 -0
- package/build/dist/Server/Services/DatabaseServerFeedService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js +272 -0
- package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerLabelRuleService.js +13 -0
- package/build/dist/Server/Services/DatabaseServerLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js +340 -0
- package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js +13 -0
- package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js +160 -0
- package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerOwnerUserService.js +153 -0
- package/build/dist/Server/Services/DatabaseServerOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerService.js +3272 -0
- package/build/dist/Server/Services/DatabaseServerService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseService.js +8 -4
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerResourceService.js +30 -3
- package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
- package/build/dist/Server/Services/IncidentAlertService.js +1370 -0
- package/build/dist/Server/Services/IncidentAlertService.js.map +1 -0
- package/build/dist/Server/Services/IncidentReminderRuleService.js +73 -23
- package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +164 -12
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +21 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +18 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemService.js +88 -5
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
- package/build/dist/Server/Services/IoTDeviceService.js +111 -24
- package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +24 -9
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/PodmanResourceService.js +30 -3
- package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +75 -23
- package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -1
- package/build/dist/Server/Services/ServiceService.js +1 -1
- package/build/dist/Server/Services/ServiceService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryUsageBillingService.js +23 -2
- package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
- package/build/dist/Server/Services/UserProjectSsoConsentService.js +79 -0
- package/build/dist/Server/Services/UserProjectSsoConsentService.js.map +1 -0
- package/build/dist/Server/Services/UserService.js +13 -1
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +18 -0
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +26 -1
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +46 -0
- package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
- package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +6 -0
- package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +134 -13
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -6
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +1489 -524
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +22 -6
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +196 -6
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +36 -9
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +175 -17
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js +29 -0
- package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +211 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +82 -10
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js +8 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +20 -4
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js +442 -0
- package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +5 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js +15 -1
- package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +121 -5
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js +12 -0
- package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js +438 -36
- package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +12 -0
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +17 -18
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js +16 -10
- package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js.map +1 -1
- package/build/dist/Types/AI/AIResourceContext.js +7 -0
- package/build/dist/Types/AI/AIResourceContext.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardTemplates.js +2 -1
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js +71 -0
- package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js +1462 -0
- package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js +691 -0
- package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js +1407 -0
- package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js +60 -0
- package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js +804 -0
- package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseSystem.js +1860 -0
- package/build/dist/Types/DatabaseServer/DatabaseSystem.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js +406 -0
- package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js.map +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/Incident/IncidentAlertLink.js +34 -0
- package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -0
- package/build/dist/Types/Monitor/CephAlertTemplates.js +18 -0
- package/build/dist/Types/Monitor/CephAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/DatabaseAlertTemplates.js +1518 -0
- package/build/dist/Types/Monitor/DatabaseAlertTemplates.js.map +1 -0
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +57 -24
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/KubernetesMetricCatalog.js +104 -18
- package/build/dist/Types/Monitor/KubernetesMetricCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +80 -3
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +10 -3
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +29 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js +21 -6
- package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js.map +1 -1
- package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js +25 -0
- package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js.map +1 -1
- package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js +136 -0
- package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js.map +1 -0
- package/build/dist/Types/Permission.js +276 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +10 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +4 -0
- package/build/dist/Types/Rules/RuleRun.js.map +1 -1
- package/build/dist/Types/Rum/WebVitals.js +25 -0
- package/build/dist/Types/Rum/WebVitals.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityType.js +23 -0
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/Telemetry/ResourceFacetCatalog.js +14 -0
- package/build/dist/Types/Telemetry/ResourceFacetCatalog.js.map +1 -1
- package/build/dist/Types/Telemetry/ServiceType.js +8 -0
- package/build/dist/Types/Telemetry/ServiceType.js.map +1 -1
- package/build/dist/UI/Components/Banner/Banner.js +1 -1
- package/build/dist/UI/Components/Banner/Banner.js.map +1 -1
- package/build/dist/UI/Components/Card/Card.js +1 -1
- package/build/dist/UI/Components/Card/Card.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +2 -2
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/CommandPalette/CommandPalette.js +1 -1
- package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -1
- package/build/dist/UI/Components/Date/StartAndEndDate.js +5 -5
- package/build/dist/UI/Components/Date/StartAndEndDate.js.map +1 -1
- package/build/dist/UI/Components/Detail/FieldLabel.js +2 -0
- package/build/dist/UI/Components/Detail/FieldLabel.js.map +1 -1
- package/build/dist/UI/Components/Diagram/HorizontalStepChain.js +1 -1
- package/build/dist/UI/Components/Diagram/HorizontalStepChain.js.map +1 -1
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js +1 -1
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -1
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
- package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js +1 -1
- package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js.map +1 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js +22 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js +1 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +2 -2
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +70 -18
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBar.js +9 -2
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +2 -2
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Components/Page/Page.js +1 -1
- package/build/dist/UI/Components/Page/Page.js.map +1 -1
- package/build/dist/UI/Components/Pagination/Pagination.js +3 -3
- package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
- package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js +1 -1
- package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenu.js +4 -1
- package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +3 -1
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceList.js +6 -6
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
- package/build/dist/UI/Components/Table/TableHeader.js +18 -2
- package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/InfoTooltip.js +28 -0
- package/build/dist/UI/Components/Tooltip/InfoTooltip.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +1 -1
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentsModal.js +2 -2
- package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js +1 -1
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -1
- package/build/dist/UI/Utils/Navigation.js +66 -33
- package/build/dist/UI/Utils/Navigation.js.map +1 -1
- package/build/dist/UI/Utils/PermissionGate.js +4 -2
- package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
- package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +7 -0
- package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -1
- package/build/dist/UI/Utils/TelemetryService.js +3 -2
- package/build/dist/UI/Utils/TelemetryService.js.map +1 -1
- package/build/dist/Utils/Incident/IncidentFromAlerts.js +306 -0
- package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -0
- package/build/dist/Utils/LabelRuleImportExport.js +2 -0
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/Utils/MetricUnitUtil.js +77 -13
- package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/MetricValueFormatter.js +58 -13
- package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -1
- package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js +161 -0
- package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/PlatformResourceIdentity.js +323 -0
- package/build/dist/Utils/Monitor/PlatformResourceIdentity.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js +270 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +23 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js +61 -0
- package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js.map +1 -1
- package/build/dist/Utils/Rum/SessionTraceState.js +154 -0
- package/build/dist/Utils/Rum/SessionTraceState.js.map +1 -0
- package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js +178 -0
- package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js.map +1 -0
- package/build/dist/Utils/Telemetry/EntityKey.js +133 -0
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- package/build/dist/Utils/ValueFormatter.js +15 -0
- package/build/dist/Utils/ValueFormatter.js.map +1 -1
- package/package.json +1 -1
- package/tsconfig.tests.json +7 -5
|
@@ -0,0 +1,1284 @@
|
|
|
1
|
+
import Semaphore, {
|
|
2
|
+
SemaphoreMutex,
|
|
3
|
+
} from "../../../Server/Infrastructure/Semaphore";
|
|
4
|
+
import AlertFeedService from "../../../Server/Services/AlertFeedService";
|
|
5
|
+
import AlertService from "../../../Server/Services/AlertService";
|
|
6
|
+
import AutoRemediationRuleEngineService from "../../../Server/Services/AutoRemediationRuleEngineService";
|
|
7
|
+
import CustomFieldMappingService from "../../../Server/Services/CustomFieldMappingService";
|
|
8
|
+
import IncidentAlertService, {
|
|
9
|
+
LinkAlertsToIncidentResult,
|
|
10
|
+
} from "../../../Server/Services/IncidentAlertService";
|
|
11
|
+
import IncidentFeedService from "../../../Server/Services/IncidentFeedService";
|
|
12
|
+
import IncidentGroupingEngineService from "../../../Server/Services/IncidentGroupingEngineService";
|
|
13
|
+
import IncidentLabelRuleEngineService from "../../../Server/Services/IncidentLabelRuleEngineService";
|
|
14
|
+
import IncidentMeasurementValueService from "../../../Server/Services/IncidentMeasurementValueService";
|
|
15
|
+
import IncidentOnCallRuleEngineService from "../../../Server/Services/IncidentOnCallRuleEngineService";
|
|
16
|
+
import IncidentOwnerRuleEngineService from "../../../Server/Services/IncidentOwnerRuleEngineService";
|
|
17
|
+
import IncidentPrivacyRuleEngineService from "../../../Server/Services/IncidentPrivacyRuleEngineService";
|
|
18
|
+
import IncidentService from "../../../Server/Services/IncidentService";
|
|
19
|
+
import IncidentSlaService from "../../../Server/Services/IncidentSlaService";
|
|
20
|
+
import IncidentStateService from "../../../Server/Services/IncidentStateService";
|
|
21
|
+
import IncidentStateTimelineService from "../../../Server/Services/IncidentStateTimelineService";
|
|
22
|
+
import ProjectService from "../../../Server/Services/ProjectService";
|
|
23
|
+
import RunbookRuleEngineService from "../../../Server/Services/RunbookRuleEngineService";
|
|
24
|
+
import UserService from "../../../Server/Services/UserService";
|
|
25
|
+
import { OnCreate } from "../../../Server/Types/Database/Hooks";
|
|
26
|
+
import AIIncidentInvestigationRunner from "../../../Server/Utils/AI/SRE/IncidentInvestigationRunner";
|
|
27
|
+
import ProjectScopedReferenceValidator from "../../../Server/Utils/Database/ProjectScopedReferenceValidator";
|
|
28
|
+
import logger from "../../../Server/Utils/Logger";
|
|
29
|
+
import ProductAnalytics from "../../../Server/Utils/ProductAnalytics";
|
|
30
|
+
import SloRecordReferenceValidator from "../../../Server/Utils/Slo/SloRecordReferenceValidator";
|
|
31
|
+
import Alert from "../../../Models/DatabaseModels/Alert";
|
|
32
|
+
import { AlertFeedEventType } from "../../../Models/DatabaseModels/AlertFeed";
|
|
33
|
+
import Incident from "../../../Models/DatabaseModels/Incident";
|
|
34
|
+
import IncidentAlert from "../../../Models/DatabaseModels/IncidentAlert";
|
|
35
|
+
import { IncidentFeedEventType } from "../../../Models/DatabaseModels/IncidentFeed";
|
|
36
|
+
import IncidentState from "../../../Models/DatabaseModels/IncidentState";
|
|
37
|
+
import IncidentStateTimeline from "../../../Models/DatabaseModels/IncidentStateTimeline";
|
|
38
|
+
import URL from "../../../Types/API/URL";
|
|
39
|
+
import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
40
|
+
import OneUptimeDate from "../../../Types/Date";
|
|
41
|
+
import { INCIDENT_ALERT_IDS_TO_LINK_KEY } from "../../../Types/Incident/IncidentAlertLink";
|
|
42
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
43
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
44
|
+
import PositiveNumber from "../../../Types/PositiveNumber";
|
|
45
|
+
import Permission, {
|
|
46
|
+
UserTenantAccessPermission,
|
|
47
|
+
} from "../../../Types/Permission";
|
|
48
|
+
import UserType from "../../../Types/UserType";
|
|
49
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
50
|
+
import { FindOperator } from "typeorm";
|
|
51
|
+
|
|
52
|
+
/*
|
|
53
|
+
* Declaring an incident from alerts, and carrying incident state changes over
|
|
54
|
+
* to linked alerts, at the two places they hook into existing services:
|
|
55
|
+
*
|
|
56
|
+
* - IncidentService.onBeforeCreate checks miscDataProps.alertIdsToLink BEFORE
|
|
57
|
+
* the incident number is taken, so a bad id neither burns a number nor
|
|
58
|
+
* leaves an incident behind; onCreateSuccess links the alerts once the
|
|
59
|
+
* incident exists, and a failing link never fails the incident;
|
|
60
|
+
* - IncidentStateTimelineService.onCreateSuccess hands the incident's new
|
|
61
|
+
* CURRENT state to the linked-alert cascade, after its mutex is released,
|
|
62
|
+
* and never waits on it or fails because of it.
|
|
63
|
+
*
|
|
64
|
+
* Everything around those hooks is stubbed; the cascade and the link helpers
|
|
65
|
+
* have their own tests.
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
69
|
+
"0194c3a9-0000-4000-8000-000000000001",
|
|
70
|
+
);
|
|
71
|
+
const INCIDENT_ID: ObjectID = new ObjectID(
|
|
72
|
+
"0194c3a9-0000-4000-8000-0000000000a1",
|
|
73
|
+
);
|
|
74
|
+
const ALERT_ID: string = "0194c3a9-0000-4000-8000-0000000000b1";
|
|
75
|
+
const ALERT_ID_2: string = "0194c3a9-0000-4000-8000-0000000000b2";
|
|
76
|
+
const FOREIGN_ALERT_ID: string = "0194c3a9-0000-4000-8000-0000000000bf";
|
|
77
|
+
const USER_ID: ObjectID = new ObjectID("0194c3a9-0000-4000-8000-0000000000c1");
|
|
78
|
+
const OTHER_USER_ID: ObjectID = new ObjectID(
|
|
79
|
+
"0194c3a9-0000-4000-8000-0000000000c2",
|
|
80
|
+
);
|
|
81
|
+
const CREATED_STATE_ID: string = "0194c3a9-0000-4000-8000-0000000000d1";
|
|
82
|
+
const ACKNOWLEDGED_STATE_ID: string = "0194c3a9-0000-4000-8000-0000000000d2";
|
|
83
|
+
const SEVERITY_ID: ObjectID = new ObjectID(
|
|
84
|
+
"0194c3a9-0000-4000-8000-0000000000e1",
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
type HookFunction = (...args: Array<unknown>) => Promise<unknown>;
|
|
88
|
+
|
|
89
|
+
function callHook<T>(
|
|
90
|
+
service: unknown,
|
|
91
|
+
name: string,
|
|
92
|
+
...args: Array<unknown>
|
|
93
|
+
): Promise<T> {
|
|
94
|
+
const hooks: Record<string, HookFunction> = service as Record<
|
|
95
|
+
string,
|
|
96
|
+
HookFunction
|
|
97
|
+
>;
|
|
98
|
+
return hooks[name]!.apply(service, args) as Promise<T>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function userProps(permission: Permission): DatabaseCommonInteractionProps {
|
|
102
|
+
const tenantPermission: UserTenantAccessPermission = {
|
|
103
|
+
projectId: PROJECT_ID,
|
|
104
|
+
_type: "UserTenantAccessPermission",
|
|
105
|
+
permissions: [
|
|
106
|
+
{
|
|
107
|
+
_type: "UserPermission",
|
|
108
|
+
permission: permission,
|
|
109
|
+
labelIds: [],
|
|
110
|
+
isBlockPermission: false,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
tenantId: PROJECT_ID,
|
|
117
|
+
userId: USER_ID,
|
|
118
|
+
userType: UserType.User,
|
|
119
|
+
userTenantAccessPermission: {
|
|
120
|
+
[PROJECT_ID.toString()]: tenantPermission,
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// An API key: a project tenant and no user.
|
|
126
|
+
function apiKeyProps(): DatabaseCommonInteractionProps {
|
|
127
|
+
const props: DatabaseCommonInteractionProps = userProps(
|
|
128
|
+
Permission.ProjectMember,
|
|
129
|
+
);
|
|
130
|
+
delete props.userId;
|
|
131
|
+
props.userType = UserType.API;
|
|
132
|
+
return props;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function settle(): Promise<void> {
|
|
136
|
+
// Let fire-and-forget chains run to completion while the stubs are live.
|
|
137
|
+
for (let i: number = 0; i < 5; i++) {
|
|
138
|
+
await new Promise<void>((resolve: () => void) => {
|
|
139
|
+
setTimeout(resolve, 0);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let errorLog: jest.SpyInstance;
|
|
145
|
+
|
|
146
|
+
beforeEach(() => {
|
|
147
|
+
errorLog = jest.spyOn(logger, "error").mockImplementation((() => {
|
|
148
|
+
// quiet
|
|
149
|
+
}) as never);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
afterEach(async () => {
|
|
153
|
+
await settle();
|
|
154
|
+
jest.restoreAllMocks();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe("IncidentService.onBeforeCreate with alerts to link", () => {
|
|
158
|
+
let counter: jest.SpyInstance;
|
|
159
|
+
let validate: jest.SpyInstance;
|
|
160
|
+
|
|
161
|
+
beforeEach(() => {
|
|
162
|
+
const createdState: IncidentState = new IncidentState();
|
|
163
|
+
createdState._id = CREATED_STATE_ID;
|
|
164
|
+
|
|
165
|
+
jest
|
|
166
|
+
.spyOn(IncidentStateService, "findOneBy")
|
|
167
|
+
.mockResolvedValue(createdState as never);
|
|
168
|
+
jest
|
|
169
|
+
.spyOn(
|
|
170
|
+
ProjectScopedReferenceValidator,
|
|
171
|
+
"validateReferencesBelongToProject",
|
|
172
|
+
)
|
|
173
|
+
.mockResolvedValue(undefined as never);
|
|
174
|
+
jest
|
|
175
|
+
.spyOn(
|
|
176
|
+
SloRecordReferenceValidator,
|
|
177
|
+
"validateServiceLevelObjectivesBelongToProject",
|
|
178
|
+
)
|
|
179
|
+
.mockResolvedValue(undefined as never);
|
|
180
|
+
jest
|
|
181
|
+
.spyOn(CustomFieldMappingService, "applyMappingsToCreate")
|
|
182
|
+
.mockResolvedValue(undefined as never);
|
|
183
|
+
jest
|
|
184
|
+
.spyOn(UserService, "getUserMarkdownString")
|
|
185
|
+
.mockResolvedValue("**A responder**" as never);
|
|
186
|
+
|
|
187
|
+
counter = jest
|
|
188
|
+
.spyOn(ProjectService, "incrementAndGetIncidentCounter")
|
|
189
|
+
.mockResolvedValue({ counter: 17, prefix: "INC-" } as never);
|
|
190
|
+
|
|
191
|
+
// The project's alerts: ALERT_ID and ALERT_ID_2. FOREIGN_ALERT_ID is not.
|
|
192
|
+
jest.spyOn(AlertService, "findBy").mockImplementation((async (args: {
|
|
193
|
+
query: { _id: unknown };
|
|
194
|
+
}): Promise<Array<Alert>> => {
|
|
195
|
+
const ids: Array<string> = Object.values(
|
|
196
|
+
(args.query._id as FindOperator<unknown>).objectLiteralParameters || {},
|
|
197
|
+
)[0] as Array<string>;
|
|
198
|
+
|
|
199
|
+
return ids
|
|
200
|
+
.filter((id: string) => {
|
|
201
|
+
return id !== FOREIGN_ALERT_ID;
|
|
202
|
+
})
|
|
203
|
+
.map((id: string) => {
|
|
204
|
+
const alert: Alert = new Alert();
|
|
205
|
+
alert._id = id;
|
|
206
|
+
return alert;
|
|
207
|
+
});
|
|
208
|
+
}) as never);
|
|
209
|
+
|
|
210
|
+
validate = jest.spyOn(
|
|
211
|
+
IncidentAlertService,
|
|
212
|
+
"validateAlertIdsForNewIncident",
|
|
213
|
+
);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
function buildIncident(): Incident {
|
|
217
|
+
const incident: Incident = new Incident();
|
|
218
|
+
incident.projectId = PROJECT_ID;
|
|
219
|
+
incident.title = "Checkout is failing";
|
|
220
|
+
incident.incidentSeverityId = SEVERITY_ID;
|
|
221
|
+
return incident;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function onBeforeCreate(
|
|
225
|
+
miscDataProps: JSONObject | undefined,
|
|
226
|
+
props: DatabaseCommonInteractionProps = userProps(Permission.ProjectMember),
|
|
227
|
+
): Promise<OnCreate<Incident>> {
|
|
228
|
+
return callHook<OnCreate<Incident>>(IncidentService, "onBeforeCreate", {
|
|
229
|
+
data: buildIncident(),
|
|
230
|
+
miscDataProps: miscDataProps,
|
|
231
|
+
props: props,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
test("without miscDataProps nothing is validated or carried forward", async () => {
|
|
236
|
+
const result: OnCreate<Incident> = await onBeforeCreate(undefined);
|
|
237
|
+
|
|
238
|
+
expect(validate).not.toHaveBeenCalled();
|
|
239
|
+
expect(result.carryForward).toBeNull();
|
|
240
|
+
expect(counter).toHaveBeenCalledTimes(1);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("miscDataProps with other keys (owners) leave the alerts alone", async () => {
|
|
244
|
+
const result: OnCreate<Incident> = await onBeforeCreate({
|
|
245
|
+
ownerUsers: [USER_ID.toString()],
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
expect(validate).not.toHaveBeenCalled();
|
|
249
|
+
expect(result.carryForward).toBeNull();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("a null list is treated as no list", async () => {
|
|
253
|
+
const result: OnCreate<Incident> = await onBeforeCreate({
|
|
254
|
+
[INCIDENT_ALERT_IDS_TO_LINK_KEY]: null,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
expect(validate).not.toHaveBeenCalled();
|
|
258
|
+
expect(result.carryForward).toBeNull();
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test.each([
|
|
262
|
+
["not a list", ALERT_ID, "must be an array of alert ids"],
|
|
263
|
+
[
|
|
264
|
+
"a list holding something other than an id",
|
|
265
|
+
[ALERT_ID, 7],
|
|
266
|
+
"must only contain alert ids",
|
|
267
|
+
],
|
|
268
|
+
[
|
|
269
|
+
"a list holding a malformed id",
|
|
270
|
+
["not-a-uuid"],
|
|
271
|
+
"must only contain alert ids",
|
|
272
|
+
],
|
|
273
|
+
["an empty list", [], "Please select at least one alert"],
|
|
274
|
+
[
|
|
275
|
+
"more than 50 alerts",
|
|
276
|
+
Array.from({ length: 51 }, () => {
|
|
277
|
+
return ObjectID.generate().toString();
|
|
278
|
+
}),
|
|
279
|
+
"You can link at most 50 alerts",
|
|
280
|
+
],
|
|
281
|
+
[
|
|
282
|
+
"an alert from another project",
|
|
283
|
+
[ALERT_ID, FOREIGN_ALERT_ID],
|
|
284
|
+
"do not exist in this project, or you do not have access to them",
|
|
285
|
+
],
|
|
286
|
+
])(
|
|
287
|
+
"%s is refused before an incident number is taken",
|
|
288
|
+
async (_label: string, alertIds: unknown, message: string) => {
|
|
289
|
+
await expect(
|
|
290
|
+
onBeforeCreate({ [INCIDENT_ALERT_IDS_TO_LINK_KEY]: alertIds as never }),
|
|
291
|
+
).rejects.toThrow(message);
|
|
292
|
+
|
|
293
|
+
expect(counter).not.toHaveBeenCalled();
|
|
294
|
+
},
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
test("a caller who may not link alerts is refused before an incident number is taken", async () => {
|
|
298
|
+
await expect(
|
|
299
|
+
onBeforeCreate(
|
|
300
|
+
{ [INCIDENT_ALERT_IDS_TO_LINK_KEY]: [ALERT_ID] },
|
|
301
|
+
userProps(Permission.IncidentViewer),
|
|
302
|
+
),
|
|
303
|
+
).rejects.toThrow(
|
|
304
|
+
"You do not have permission to link alerts to incidents in this project.",
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
expect(counter).not.toHaveBeenCalled();
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test("valid alert ids are checked as the caller, then carried to onCreateSuccess", async () => {
|
|
311
|
+
const props: DatabaseCommonInteractionProps = userProps(
|
|
312
|
+
Permission.ProjectMember,
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
const result: OnCreate<Incident> = await onBeforeCreate(
|
|
316
|
+
{
|
|
317
|
+
[INCIDENT_ALERT_IDS_TO_LINK_KEY]: [
|
|
318
|
+
ALERT_ID,
|
|
319
|
+
ALERT_ID_2,
|
|
320
|
+
ALERT_ID.toUpperCase(),
|
|
321
|
+
],
|
|
322
|
+
},
|
|
323
|
+
props,
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
expect(validate).toHaveBeenCalledTimes(1);
|
|
327
|
+
|
|
328
|
+
const args: {
|
|
329
|
+
projectId: ObjectID;
|
|
330
|
+
alertIds: unknown;
|
|
331
|
+
props: DatabaseCommonInteractionProps;
|
|
332
|
+
} = validate.mock.calls[0]![0];
|
|
333
|
+
|
|
334
|
+
expect(args.projectId.toString()).toBe(PROJECT_ID.toString());
|
|
335
|
+
expect(args.props).toBe(props);
|
|
336
|
+
|
|
337
|
+
// Validated before the number was taken.
|
|
338
|
+
expect(validate.mock.invocationCallOrder[0]!).toBeLessThan(
|
|
339
|
+
counter.mock.invocationCallOrder[0]!,
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
const carried: { alertIdsToLink: Array<ObjectID> } =
|
|
343
|
+
result.carryForward as { alertIdsToLink: Array<ObjectID> };
|
|
344
|
+
|
|
345
|
+
expect(carried.alertIdsToLink.map(String)).toEqual([ALERT_ID, ALERT_ID_2]);
|
|
346
|
+
expect(result.createBy.data.incidentNumber).toBe(17);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test("a root caller (API automation run as root) is validated too", async () => {
|
|
350
|
+
const result: OnCreate<Incident> = await onBeforeCreate(
|
|
351
|
+
{ [INCIDENT_ALERT_IDS_TO_LINK_KEY]: [ALERT_ID] },
|
|
352
|
+
{ isRoot: true },
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
expect(
|
|
356
|
+
(result.carryForward as { alertIdsToLink: Array<ObjectID> })
|
|
357
|
+
.alertIdsToLink,
|
|
358
|
+
).toHaveLength(1);
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
describe("IncidentService.onCreateSuccess links the alerts it was declared with", () => {
|
|
363
|
+
let link: jest.SpyInstance;
|
|
364
|
+
let summary: jest.SpyInstance;
|
|
365
|
+
let copyOwners: jest.SpyInstance;
|
|
366
|
+
const chain: Record<string, jest.SpyInstance> = {};
|
|
367
|
+
|
|
368
|
+
beforeEach(() => {
|
|
369
|
+
jest.spyOn(ProductAnalytics, "captureForUser").mockImplementation((() => {
|
|
370
|
+
// no analytics in tests
|
|
371
|
+
}) as never);
|
|
372
|
+
|
|
373
|
+
const reRead: Incident = new Incident();
|
|
374
|
+
reRead._id = INCIDENT_ID.toString();
|
|
375
|
+
reRead.projectId = PROJECT_ID;
|
|
376
|
+
jest
|
|
377
|
+
.spyOn(IncidentService, "findOneById")
|
|
378
|
+
.mockResolvedValue(reRead as never);
|
|
379
|
+
|
|
380
|
+
// The un-awaited side-effect chain.
|
|
381
|
+
const service: Record<string, unknown> =
|
|
382
|
+
IncidentService as unknown as Record<string, unknown>;
|
|
383
|
+
for (const method of [
|
|
384
|
+
"handleIncidentWorkspaceOperationsAsync",
|
|
385
|
+
"createIncidentFeedAsync",
|
|
386
|
+
"handleIncidentStateChangeAsync",
|
|
387
|
+
"disableActiveMonitoringIfManualIncident",
|
|
388
|
+
"refreshReminderSchedule",
|
|
389
|
+
]) {
|
|
390
|
+
chain[method] = jest
|
|
391
|
+
.spyOn(service as Record<string, () => Promise<void>>, method)
|
|
392
|
+
.mockResolvedValue(undefined as never);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
jest
|
|
396
|
+
.spyOn(IncidentPrivacyRuleEngineService, "applyRulesToIncident")
|
|
397
|
+
.mockResolvedValue(false as never);
|
|
398
|
+
jest
|
|
399
|
+
.spyOn(IncidentOwnerRuleEngineService, "applyRulesToIncident")
|
|
400
|
+
.mockResolvedValue(undefined as never);
|
|
401
|
+
jest
|
|
402
|
+
.spyOn(IncidentLabelRuleEngineService, "applyRulesToIncident")
|
|
403
|
+
.mockResolvedValue(undefined as never);
|
|
404
|
+
jest
|
|
405
|
+
.spyOn(IncidentOnCallRuleEngineService, "applyRulesToIncident")
|
|
406
|
+
.mockResolvedValue(undefined as never);
|
|
407
|
+
jest
|
|
408
|
+
.spyOn(RunbookRuleEngineService, "applyRulesToIncident")
|
|
409
|
+
.mockResolvedValue(undefined as never);
|
|
410
|
+
jest
|
|
411
|
+
.spyOn(IncidentGroupingEngineService, "processIncident")
|
|
412
|
+
.mockResolvedValue({} as never);
|
|
413
|
+
jest
|
|
414
|
+
.spyOn(IncidentSlaService, "createSlaForIncident")
|
|
415
|
+
.mockResolvedValue(undefined as never);
|
|
416
|
+
jest
|
|
417
|
+
.spyOn(AIIncidentInvestigationRunner, "investigateNewIncident")
|
|
418
|
+
.mockResolvedValue(false as never);
|
|
419
|
+
jest
|
|
420
|
+
.spyOn(AutoRemediationRuleEngineService, "applyRulesToIncident")
|
|
421
|
+
.mockResolvedValue(undefined as never);
|
|
422
|
+
|
|
423
|
+
link = jest
|
|
424
|
+
.spyOn(IncidentAlertService, "linkAlertsToIncident")
|
|
425
|
+
.mockResolvedValue({
|
|
426
|
+
linkedAlertIds: [],
|
|
427
|
+
alreadyLinkedAlertIds: [],
|
|
428
|
+
failed: [],
|
|
429
|
+
} as never);
|
|
430
|
+
summary = jest
|
|
431
|
+
.spyOn(IncidentAlertService, "createDeclaredFromAlertsFeedItem")
|
|
432
|
+
.mockResolvedValue(undefined as never);
|
|
433
|
+
copyOwners = jest
|
|
434
|
+
.spyOn(IncidentAlertService, "copyAlertOwnersToIncident")
|
|
435
|
+
.mockResolvedValue({ userIds: [], teamIds: [] } as never);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
function createdIncident(
|
|
439
|
+
createdByUserId?: ObjectID,
|
|
440
|
+
isPrivate: boolean = false,
|
|
441
|
+
): Incident {
|
|
442
|
+
const incident: Incident = new Incident();
|
|
443
|
+
incident._id = INCIDENT_ID.toString();
|
|
444
|
+
incident.projectId = PROJECT_ID;
|
|
445
|
+
incident.title = "Checkout is failing";
|
|
446
|
+
incident.declaredAt = OneUptimeDate.getCurrentDate();
|
|
447
|
+
incident.isPrivate = isPrivate;
|
|
448
|
+
if (createdByUserId) {
|
|
449
|
+
incident.createdByUserId = createdByUserId;
|
|
450
|
+
}
|
|
451
|
+
return incident;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function onCreateSuccess(
|
|
455
|
+
carryForward: unknown,
|
|
456
|
+
createdItem: Incident,
|
|
457
|
+
props: DatabaseCommonInteractionProps = { isRoot: true },
|
|
458
|
+
): Promise<Incident> {
|
|
459
|
+
return callHook<Incident>(
|
|
460
|
+
IncidentService,
|
|
461
|
+
"onCreateSuccess",
|
|
462
|
+
{
|
|
463
|
+
createBy: { data: createdItem, props: props },
|
|
464
|
+
carryForward: carryForward,
|
|
465
|
+
},
|
|
466
|
+
createdItem,
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
test("links every validated alert as root, recording the declaring user", async () => {
|
|
471
|
+
const alertIds: Array<ObjectID> = [
|
|
472
|
+
new ObjectID(ALERT_ID),
|
|
473
|
+
new ObjectID(ALERT_ID_2),
|
|
474
|
+
];
|
|
475
|
+
const incident: Incident = createdIncident(USER_ID);
|
|
476
|
+
|
|
477
|
+
await expect(
|
|
478
|
+
onCreateSuccess({ alertIdsToLink: alertIds }, incident),
|
|
479
|
+
).resolves.toBe(incident);
|
|
480
|
+
|
|
481
|
+
expect(link).toHaveBeenCalledTimes(1);
|
|
482
|
+
expect(link).toHaveBeenCalledWith({
|
|
483
|
+
projectId: PROJECT_ID,
|
|
484
|
+
incidentId: incident.id,
|
|
485
|
+
alertIds: alertIds,
|
|
486
|
+
createdByUserId: USER_ID,
|
|
487
|
+
declaredWithIncident: true,
|
|
488
|
+
props: { isRoot: true },
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
test("a user who declares the incident is recorded as the one who linked the alerts", async () => {
|
|
493
|
+
await onCreateSuccess(
|
|
494
|
+
{ alertIdsToLink: [new ObjectID(ALERT_ID)] },
|
|
495
|
+
createdIncident(),
|
|
496
|
+
{ userId: OTHER_USER_ID, tenantId: PROJECT_ID },
|
|
497
|
+
);
|
|
498
|
+
|
|
499
|
+
expect(link.mock.calls[0]![0].createdByUserId).toBe(OTHER_USER_ID);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
test("a user caller is recorded as themselves, whatever the incident's createdByUserId says", async () => {
|
|
503
|
+
await onCreateSuccess(
|
|
504
|
+
{ alertIdsToLink: [new ObjectID(ALERT_ID)] },
|
|
505
|
+
createdIncident(USER_ID),
|
|
506
|
+
{ userId: OTHER_USER_ID, tenantId: PROJECT_ID },
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
expect(link.mock.calls[0]![0].createdByUserId).toBe(OTHER_USER_ID);
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
test("an API key cannot name somebody else as the one who linked the alerts", async () => {
|
|
513
|
+
// The key sent data.createdByUserId naming another user.
|
|
514
|
+
await onCreateSuccess(
|
|
515
|
+
{ alertIdsToLink: [new ObjectID(ALERT_ID)] },
|
|
516
|
+
createdIncident(OTHER_USER_ID),
|
|
517
|
+
apiKeyProps(),
|
|
518
|
+
);
|
|
519
|
+
|
|
520
|
+
expect(link).toHaveBeenCalledTimes(1);
|
|
521
|
+
expect(link.mock.calls[0]![0].createdByUserId).toBeUndefined();
|
|
522
|
+
// The links are still written as root, and still declared.
|
|
523
|
+
expect(link.mock.calls[0]![0].props).toEqual({ isRoot: true });
|
|
524
|
+
expect(link.mock.calls[0]![0].declaredWithIncident).toBe(true);
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
test("the links exist by the time the incident is returned", async () => {
|
|
528
|
+
let linked: boolean = false;
|
|
529
|
+
|
|
530
|
+
link.mockImplementation((async (): Promise<LinkAlertsToIncidentResult> => {
|
|
531
|
+
await new Promise<void>((resolve: () => void) => {
|
|
532
|
+
setTimeout(resolve, 5);
|
|
533
|
+
});
|
|
534
|
+
linked = true;
|
|
535
|
+
return { linkedAlertIds: [], alreadyLinkedAlertIds: [], failed: [] };
|
|
536
|
+
}) as never);
|
|
537
|
+
|
|
538
|
+
await onCreateSuccess(
|
|
539
|
+
{ alertIdsToLink: [new ObjectID(ALERT_ID)] },
|
|
540
|
+
createdIncident(),
|
|
541
|
+
);
|
|
542
|
+
|
|
543
|
+
expect(linked).toBe(true);
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
test("a link that throws never fails the incident", async () => {
|
|
547
|
+
link.mockRejectedValue(new Error("database unavailable") as never);
|
|
548
|
+
const incident: Incident = createdIncident();
|
|
549
|
+
|
|
550
|
+
await expect(
|
|
551
|
+
onCreateSuccess({ alertIdsToLink: [new ObjectID(ALERT_ID)] }, incident),
|
|
552
|
+
).resolves.toBe(incident);
|
|
553
|
+
|
|
554
|
+
expect(errorLog).toHaveBeenCalledWith(
|
|
555
|
+
expect.stringContaining("Linking the alerts an incident was declared"),
|
|
556
|
+
expect.anything(),
|
|
557
|
+
);
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
test("alerts that could not be linked are logged, and the incident is still returned", async () => {
|
|
561
|
+
link.mockResolvedValue({
|
|
562
|
+
linkedAlertIds: [new ObjectID(ALERT_ID)],
|
|
563
|
+
alreadyLinkedAlertIds: [],
|
|
564
|
+
failed: [{ alertId: new ObjectID(ALERT_ID_2), message: "gone" }],
|
|
565
|
+
} as never);
|
|
566
|
+
const incident: Incident = createdIncident();
|
|
567
|
+
|
|
568
|
+
await expect(
|
|
569
|
+
onCreateSuccess(
|
|
570
|
+
{
|
|
571
|
+
alertIdsToLink: [new ObjectID(ALERT_ID), new ObjectID(ALERT_ID_2)],
|
|
572
|
+
},
|
|
573
|
+
incident,
|
|
574
|
+
),
|
|
575
|
+
).resolves.toBe(incident);
|
|
576
|
+
|
|
577
|
+
expect(errorLog).toHaveBeenCalledWith(
|
|
578
|
+
expect.stringContaining("1 of 2 alerts could not be linked"),
|
|
579
|
+
expect.anything(),
|
|
580
|
+
);
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
test.each([
|
|
584
|
+
["no carry-forward", null],
|
|
585
|
+
["an undefined carry-forward", undefined],
|
|
586
|
+
["an empty list", { alertIdsToLink: [] }],
|
|
587
|
+
])(
|
|
588
|
+
"an incident declared with %s links nothing",
|
|
589
|
+
async (_label: string, carryForward: unknown) => {
|
|
590
|
+
await onCreateSuccess(carryForward, createdIncident(USER_ID, true));
|
|
591
|
+
await settle();
|
|
592
|
+
|
|
593
|
+
expect(link).not.toHaveBeenCalled();
|
|
594
|
+
expect(summary).not.toHaveBeenCalled();
|
|
595
|
+
expect(copyOwners).not.toHaveBeenCalled();
|
|
596
|
+
},
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
describe("one announcement instead of one per alert", () => {
|
|
600
|
+
const alertIds: Array<ObjectID> = [
|
|
601
|
+
new ObjectID(ALERT_ID),
|
|
602
|
+
new ObjectID(ALERT_ID_2),
|
|
603
|
+
];
|
|
604
|
+
|
|
605
|
+
function order(name: string): number {
|
|
606
|
+
return chain[name]!.mock.invocationCallOrder[0]!;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
test("the alerts are announced once, after Incident Created and the workspace channels", async () => {
|
|
610
|
+
await onCreateSuccess(
|
|
611
|
+
{ alertIdsToLink: alertIds },
|
|
612
|
+
createdIncident(USER_ID),
|
|
613
|
+
);
|
|
614
|
+
await settle();
|
|
615
|
+
|
|
616
|
+
expect(summary).toHaveBeenCalledTimes(1);
|
|
617
|
+
expect(summary).toHaveBeenCalledWith({
|
|
618
|
+
projectId: PROJECT_ID,
|
|
619
|
+
incidentId: INCIDENT_ID,
|
|
620
|
+
alertIds: alertIds,
|
|
621
|
+
actorUserId: USER_ID,
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
const summaryOrder: number = summary.mock.invocationCallOrder[0]!;
|
|
625
|
+
|
|
626
|
+
expect(order("handleIncidentWorkspaceOperationsAsync")).toBeLessThan(
|
|
627
|
+
summaryOrder,
|
|
628
|
+
);
|
|
629
|
+
expect(order("createIncidentFeedAsync")).toBeLessThan(summaryOrder);
|
|
630
|
+
// ...and it is the very next step, ahead of the rest of the chain.
|
|
631
|
+
expect(summaryOrder).toBeLessThan(
|
|
632
|
+
order("handleIncidentStateChangeAsync"),
|
|
633
|
+
);
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
test("the links themselves are written without incident-side entries", async () => {
|
|
637
|
+
await onCreateSuccess(
|
|
638
|
+
{ alertIdsToLink: alertIds },
|
|
639
|
+
createdIncident(USER_ID),
|
|
640
|
+
);
|
|
641
|
+
|
|
642
|
+
expect(link.mock.calls[0]![0].declaredWithIncident).toBe(true);
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
test("a user's announcement is theirs, an API key's names nobody", async () => {
|
|
646
|
+
await onCreateSuccess(
|
|
647
|
+
{ alertIdsToLink: alertIds },
|
|
648
|
+
createdIncident(USER_ID),
|
|
649
|
+
{ userId: OTHER_USER_ID, tenantId: PROJECT_ID },
|
|
650
|
+
);
|
|
651
|
+
await settle();
|
|
652
|
+
|
|
653
|
+
expect(summary.mock.calls[0]![0].actorUserId).toBe(OTHER_USER_ID);
|
|
654
|
+
|
|
655
|
+
summary.mockClear();
|
|
656
|
+
|
|
657
|
+
await onCreateSuccess(
|
|
658
|
+
{ alertIdsToLink: alertIds },
|
|
659
|
+
createdIncident(OTHER_USER_ID),
|
|
660
|
+
apiKeyProps(),
|
|
661
|
+
);
|
|
662
|
+
await settle();
|
|
663
|
+
|
|
664
|
+
expect(summary).toHaveBeenCalledTimes(1);
|
|
665
|
+
expect(summary.mock.calls[0]![0].actorUserId).toBeUndefined();
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
test("it does not wait for the links", async () => {
|
|
669
|
+
let finishLinking: () => void = (): void => {
|
|
670
|
+
// replaced once linking starts
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
link.mockImplementation(
|
|
674
|
+
(async (): Promise<LinkAlertsToIncidentResult> => {
|
|
675
|
+
await new Promise<void>((resolve: () => void) => {
|
|
676
|
+
finishLinking = resolve;
|
|
677
|
+
});
|
|
678
|
+
return { linkedAlertIds: [], alreadyLinkedAlertIds: [], failed: [] };
|
|
679
|
+
}) as never,
|
|
680
|
+
);
|
|
681
|
+
|
|
682
|
+
const created: Promise<Incident> = onCreateSuccess(
|
|
683
|
+
{ alertIdsToLink: alertIds },
|
|
684
|
+
createdIncident(USER_ID),
|
|
685
|
+
);
|
|
686
|
+
await settle();
|
|
687
|
+
|
|
688
|
+
// Still linking, already announced.
|
|
689
|
+
expect(summary).toHaveBeenCalledTimes(1);
|
|
690
|
+
|
|
691
|
+
finishLinking();
|
|
692
|
+
await created;
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
test("an announcement that fails is logged, and the rest of the chain carries on", async () => {
|
|
696
|
+
summary.mockRejectedValue(new Error("slack is down") as never);
|
|
697
|
+
|
|
698
|
+
const incident: Incident = createdIncident(USER_ID);
|
|
699
|
+
|
|
700
|
+
await expect(
|
|
701
|
+
onCreateSuccess({ alertIdsToLink: alertIds }, incident),
|
|
702
|
+
).resolves.toBe(incident);
|
|
703
|
+
await settle();
|
|
704
|
+
|
|
705
|
+
expect(errorLog).toHaveBeenCalledWith(
|
|
706
|
+
expect.stringContaining(
|
|
707
|
+
"Announcing the alerts an incident was declared from failed",
|
|
708
|
+
),
|
|
709
|
+
expect.anything(),
|
|
710
|
+
);
|
|
711
|
+
expect(chain["handleIncidentStateChangeAsync"]).toHaveBeenCalledTimes(1);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
test("a failed Incident Created entry does not stop the announcement", async () => {
|
|
715
|
+
chain["createIncidentFeedAsync"]!.mockRejectedValue(
|
|
716
|
+
new Error("feed failed") as never,
|
|
717
|
+
);
|
|
718
|
+
|
|
719
|
+
await onCreateSuccess(
|
|
720
|
+
{ alertIdsToLink: alertIds },
|
|
721
|
+
createdIncident(USER_ID),
|
|
722
|
+
);
|
|
723
|
+
await settle();
|
|
724
|
+
|
|
725
|
+
expect(summary).toHaveBeenCalledTimes(1);
|
|
726
|
+
});
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
describe("a private incident takes on the alerts' owners", () => {
|
|
730
|
+
const alertIds: Array<ObjectID> = [
|
|
731
|
+
new ObjectID(ALERT_ID),
|
|
732
|
+
new ObjectID(ALERT_ID_2),
|
|
733
|
+
];
|
|
734
|
+
|
|
735
|
+
function order(name: string): number {
|
|
736
|
+
return chain[name]!.mock.invocationCallOrder[0]!;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
test("the owners are added once the incident's channels exist, after Incident Created and the announcement", async () => {
|
|
740
|
+
await onCreateSuccess(
|
|
741
|
+
{ alertIdsToLink: alertIds },
|
|
742
|
+
createdIncident(USER_ID, true),
|
|
743
|
+
userProps(Permission.ProjectMember),
|
|
744
|
+
);
|
|
745
|
+
await settle();
|
|
746
|
+
|
|
747
|
+
expect(copyOwners).toHaveBeenCalledTimes(1);
|
|
748
|
+
expect(copyOwners).toHaveBeenCalledWith({
|
|
749
|
+
projectId: PROJECT_ID,
|
|
750
|
+
incidentId: INCIDENT_ID,
|
|
751
|
+
alertIds: alertIds,
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
/*
|
|
755
|
+
* The owners' own hooks invite them to the incident's Slack / Teams
|
|
756
|
+
* channels, so the copy must come after the step that records them.
|
|
757
|
+
*/
|
|
758
|
+
const copyOrder: number = copyOwners.mock.invocationCallOrder[0]!;
|
|
759
|
+
|
|
760
|
+
expect(order("handleIncidentWorkspaceOperationsAsync")).toBeLessThan(
|
|
761
|
+
copyOrder,
|
|
762
|
+
);
|
|
763
|
+
expect(order("createIncidentFeedAsync")).toBeLessThan(copyOrder);
|
|
764
|
+
expect(summary.mock.invocationCallOrder[0]!).toBeLessThan(copyOrder);
|
|
765
|
+
expect(copyOrder).toBeLessThan(order("handleIncidentStateChangeAsync"));
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
test("an incident a privacy rule makes private takes on the owners too", async () => {
|
|
769
|
+
jest
|
|
770
|
+
.spyOn(IncidentPrivacyRuleEngineService, "applyRulesToIncident")
|
|
771
|
+
.mockImplementation((async (incident: Incident): Promise<boolean> => {
|
|
772
|
+
incident.isPrivate = true;
|
|
773
|
+
return true;
|
|
774
|
+
}) as never);
|
|
775
|
+
|
|
776
|
+
await onCreateSuccess(
|
|
777
|
+
{ alertIdsToLink: alertIds },
|
|
778
|
+
createdIncident(USER_ID, false),
|
|
779
|
+
);
|
|
780
|
+
await settle();
|
|
781
|
+
|
|
782
|
+
expect(copyOwners).toHaveBeenCalledTimes(1);
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
test("a public incident keeps its owners as they are", async () => {
|
|
786
|
+
await onCreateSuccess(
|
|
787
|
+
{ alertIdsToLink: alertIds },
|
|
788
|
+
createdIncident(USER_ID, false),
|
|
789
|
+
);
|
|
790
|
+
await settle();
|
|
791
|
+
|
|
792
|
+
expect(copyOwners).not.toHaveBeenCalled();
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
test("an incident declared without alerts never copies owners", async () => {
|
|
796
|
+
await onCreateSuccess(null, createdIncident(USER_ID, true));
|
|
797
|
+
await settle();
|
|
798
|
+
|
|
799
|
+
expect(copyOwners).not.toHaveBeenCalled();
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
test("the owners are added even when linking fails", async () => {
|
|
803
|
+
link.mockRejectedValue(new Error("database unavailable") as never);
|
|
804
|
+
|
|
805
|
+
await onCreateSuccess(
|
|
806
|
+
{ alertIdsToLink: alertIds },
|
|
807
|
+
createdIncident(USER_ID, true),
|
|
808
|
+
);
|
|
809
|
+
await settle();
|
|
810
|
+
|
|
811
|
+
expect(copyOwners).toHaveBeenCalledTimes(1);
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
test("a failure adding them is logged, and the rest of the chain carries on", async () => {
|
|
815
|
+
copyOwners.mockRejectedValue(new Error("owners failed") as never);
|
|
816
|
+
const incident: Incident = createdIncident(USER_ID, true);
|
|
817
|
+
|
|
818
|
+
await expect(
|
|
819
|
+
onCreateSuccess({ alertIdsToLink: alertIds }, incident),
|
|
820
|
+
).resolves.toBe(incident);
|
|
821
|
+
await settle();
|
|
822
|
+
|
|
823
|
+
expect(errorLog).toHaveBeenCalledWith(
|
|
824
|
+
expect.stringContaining(
|
|
825
|
+
"Adding the owners of the alerts a private incident was declared from failed",
|
|
826
|
+
),
|
|
827
|
+
expect.anything(),
|
|
828
|
+
);
|
|
829
|
+
expect(chain["handleIncidentStateChangeAsync"]).toHaveBeenCalledTimes(1);
|
|
830
|
+
});
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
describe("linking waits for the privacy rules", () => {
|
|
834
|
+
test("the links are written only once the privacy rules have run", async () => {
|
|
835
|
+
let rulesApplied: boolean = false;
|
|
836
|
+
let rulesAppliedWhenLinking: boolean | null = null;
|
|
837
|
+
|
|
838
|
+
jest
|
|
839
|
+
.spyOn(IncidentPrivacyRuleEngineService, "applyRulesToIncident")
|
|
840
|
+
.mockImplementation((async (incident: Incident): Promise<boolean> => {
|
|
841
|
+
await new Promise<void>((resolve: () => void) => {
|
|
842
|
+
setTimeout(resolve, 5);
|
|
843
|
+
});
|
|
844
|
+
incident.isPrivate = true;
|
|
845
|
+
rulesApplied = true;
|
|
846
|
+
return true;
|
|
847
|
+
}) as never);
|
|
848
|
+
|
|
849
|
+
link.mockImplementation(
|
|
850
|
+
(async (): Promise<LinkAlertsToIncidentResult> => {
|
|
851
|
+
rulesAppliedWhenLinking = rulesApplied;
|
|
852
|
+
return { linkedAlertIds: [], alreadyLinkedAlertIds: [], failed: [] };
|
|
853
|
+
}) as never,
|
|
854
|
+
);
|
|
855
|
+
|
|
856
|
+
await onCreateSuccess(
|
|
857
|
+
{ alertIdsToLink: [new ObjectID(ALERT_ID)] },
|
|
858
|
+
createdIncident(USER_ID),
|
|
859
|
+
);
|
|
860
|
+
|
|
861
|
+
expect(link).toHaveBeenCalledTimes(1);
|
|
862
|
+
expect(rulesAppliedWhenLinking).toBe(true);
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
test("an incident declared without alerts does not wait for the privacy rules", async () => {
|
|
866
|
+
let finishRules: () => void = (): void => {
|
|
867
|
+
// replaced once the rules start
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
jest
|
|
871
|
+
.spyOn(IncidentPrivacyRuleEngineService, "applyRulesToIncident")
|
|
872
|
+
.mockImplementation((async (): Promise<boolean> => {
|
|
873
|
+
await new Promise<void>((resolve: () => void) => {
|
|
874
|
+
finishRules = resolve;
|
|
875
|
+
});
|
|
876
|
+
return false;
|
|
877
|
+
}) as never);
|
|
878
|
+
|
|
879
|
+
const incident: Incident = createdIncident(USER_ID);
|
|
880
|
+
|
|
881
|
+
// Resolves while the rules are still running.
|
|
882
|
+
await expect(onCreateSuccess(null, incident)).resolves.toBe(incident);
|
|
883
|
+
|
|
884
|
+
finishRules();
|
|
885
|
+
});
|
|
886
|
+
});
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
describe("declaring an incident from alerts, through the real link service", () => {
|
|
890
|
+
let incidentFeed: jest.SpyInstance;
|
|
891
|
+
let alertFeed: jest.SpyInstance;
|
|
892
|
+
let save: jest.Mock;
|
|
893
|
+
|
|
894
|
+
const ALERT_TITLES: Record<string, string> = {
|
|
895
|
+
[ALERT_ID]: "Checkout p95 latency is high",
|
|
896
|
+
[ALERT_ID_2]: "Payroll DB credentials exposed",
|
|
897
|
+
};
|
|
898
|
+
|
|
899
|
+
function alertRow(id: string, number: number, isPrivate: boolean): Alert {
|
|
900
|
+
const alert: Alert = new Alert();
|
|
901
|
+
alert._id = id;
|
|
902
|
+
alert.alertNumber = number;
|
|
903
|
+
alert.title = ALERT_TITLES[id]!;
|
|
904
|
+
alert.isPrivate = isPrivate;
|
|
905
|
+
return alert;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
beforeEach(() => {
|
|
909
|
+
jest.spyOn(ProductAnalytics, "captureForUser").mockImplementation((() => {
|
|
910
|
+
// no analytics in tests
|
|
911
|
+
}) as never);
|
|
912
|
+
|
|
913
|
+
const incident: Incident = new Incident();
|
|
914
|
+
incident._id = INCIDENT_ID.toString();
|
|
915
|
+
incident.projectId = PROJECT_ID;
|
|
916
|
+
incident.incidentNumber = 17;
|
|
917
|
+
incident.incidentNumberWithPrefix = "INC-17";
|
|
918
|
+
incident.title = "Checkout is failing";
|
|
919
|
+
jest
|
|
920
|
+
.spyOn(IncidentService, "findOneById")
|
|
921
|
+
.mockResolvedValue(incident as never);
|
|
922
|
+
|
|
923
|
+
const service: Record<string, unknown> =
|
|
924
|
+
IncidentService as unknown as Record<string, unknown>;
|
|
925
|
+
for (const method of [
|
|
926
|
+
"handleIncidentWorkspaceOperationsAsync",
|
|
927
|
+
"createIncidentFeedAsync",
|
|
928
|
+
"handleIncidentStateChangeAsync",
|
|
929
|
+
"disableActiveMonitoringIfManualIncident",
|
|
930
|
+
"refreshReminderSchedule",
|
|
931
|
+
]) {
|
|
932
|
+
jest
|
|
933
|
+
.spyOn(service as Record<string, () => Promise<void>>, method)
|
|
934
|
+
.mockResolvedValue(undefined as never);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
jest
|
|
938
|
+
.spyOn(IncidentPrivacyRuleEngineService, "applyRulesToIncident")
|
|
939
|
+
.mockResolvedValue(false as never);
|
|
940
|
+
jest
|
|
941
|
+
.spyOn(IncidentOwnerRuleEngineService, "applyRulesToIncident")
|
|
942
|
+
.mockResolvedValue(undefined as never);
|
|
943
|
+
jest
|
|
944
|
+
.spyOn(IncidentLabelRuleEngineService, "applyRulesToIncident")
|
|
945
|
+
.mockResolvedValue(undefined as never);
|
|
946
|
+
jest
|
|
947
|
+
.spyOn(IncidentOnCallRuleEngineService, "applyRulesToIncident")
|
|
948
|
+
.mockResolvedValue(undefined as never);
|
|
949
|
+
jest
|
|
950
|
+
.spyOn(RunbookRuleEngineService, "applyRulesToIncident")
|
|
951
|
+
.mockResolvedValue(undefined as never);
|
|
952
|
+
jest
|
|
953
|
+
.spyOn(IncidentGroupingEngineService, "processIncident")
|
|
954
|
+
.mockResolvedValue({} as never);
|
|
955
|
+
jest
|
|
956
|
+
.spyOn(IncidentSlaService, "createSlaForIncident")
|
|
957
|
+
.mockResolvedValue(undefined as never);
|
|
958
|
+
jest
|
|
959
|
+
.spyOn(AIIncidentInvestigationRunner, "investigateNewIncident")
|
|
960
|
+
.mockResolvedValue(false as never);
|
|
961
|
+
jest
|
|
962
|
+
.spyOn(AutoRemediationRuleEngineService, "applyRulesToIncident")
|
|
963
|
+
.mockResolvedValue(undefined as never);
|
|
964
|
+
|
|
965
|
+
// The link table, without a database.
|
|
966
|
+
save = jest.fn(async (row: IncidentAlert): Promise<IncidentAlert> => {
|
|
967
|
+
row._id = ObjectID.generate().toString();
|
|
968
|
+
return row;
|
|
969
|
+
});
|
|
970
|
+
jest
|
|
971
|
+
.spyOn(IncidentAlertService, "getRepository")
|
|
972
|
+
.mockReturnValue({ save: save } as never);
|
|
973
|
+
jest
|
|
974
|
+
.spyOn(IncidentAlertService, "countBy")
|
|
975
|
+
.mockResolvedValue(new PositiveNumber(0) as never);
|
|
976
|
+
jest
|
|
977
|
+
.spyOn(
|
|
978
|
+
ProjectScopedReferenceValidator,
|
|
979
|
+
"validateReferencesBelongToProject",
|
|
980
|
+
)
|
|
981
|
+
.mockResolvedValue(undefined as never);
|
|
982
|
+
jest
|
|
983
|
+
.spyOn(IncidentAlertService, "onTriggerWorkflow")
|
|
984
|
+
.mockResolvedValue(undefined as never);
|
|
985
|
+
jest
|
|
986
|
+
.spyOn(IncidentAlertService, "onTriggerRealtime")
|
|
987
|
+
.mockResolvedValue(undefined as never);
|
|
988
|
+
jest
|
|
989
|
+
.spyOn(IncidentAlertService, "syncAlertWithLinkedIncidentState")
|
|
990
|
+
.mockResolvedValue(undefined as never);
|
|
991
|
+
|
|
992
|
+
// The alerts: #3 is public, #4 is private.
|
|
993
|
+
jest.spyOn(AlertService, "findOneById").mockImplementation((async (args: {
|
|
994
|
+
id: ObjectID;
|
|
995
|
+
}): Promise<Alert> => {
|
|
996
|
+
return args.id.toString() === ALERT_ID
|
|
997
|
+
? alertRow(ALERT_ID, 3, false)
|
|
998
|
+
: alertRow(ALERT_ID_2, 4, true);
|
|
999
|
+
}) as never);
|
|
1000
|
+
jest
|
|
1001
|
+
.spyOn(AlertService, "findBy")
|
|
1002
|
+
.mockResolvedValue([
|
|
1003
|
+
alertRow(ALERT_ID_2, 4, true),
|
|
1004
|
+
alertRow(ALERT_ID, 3, false),
|
|
1005
|
+
] as never);
|
|
1006
|
+
jest
|
|
1007
|
+
.spyOn(AlertService, "getAlertLinkInDashboard")
|
|
1008
|
+
.mockImplementation((async (
|
|
1009
|
+
_projectId: ObjectID,
|
|
1010
|
+
alertId: ObjectID,
|
|
1011
|
+
): Promise<URL> => {
|
|
1012
|
+
return URL.fromString(
|
|
1013
|
+
`https://oneuptime.example/alerts/${alertId.toString()}`,
|
|
1014
|
+
);
|
|
1015
|
+
}) as never);
|
|
1016
|
+
jest
|
|
1017
|
+
.spyOn(IncidentService, "getIncidentLinkInDashboard")
|
|
1018
|
+
.mockResolvedValue(
|
|
1019
|
+
URL.fromString("https://oneuptime.example/incident") as never,
|
|
1020
|
+
);
|
|
1021
|
+
|
|
1022
|
+
incidentFeed = jest
|
|
1023
|
+
.spyOn(IncidentFeedService, "createIncidentFeedItem")
|
|
1024
|
+
.mockResolvedValue(undefined as never);
|
|
1025
|
+
alertFeed = jest
|
|
1026
|
+
.spyOn(AlertFeedService, "createAlertFeedItem")
|
|
1027
|
+
.mockResolvedValue(undefined as never);
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
function declare(props: DatabaseCommonInteractionProps): Promise<Incident> {
|
|
1031
|
+
const incident: Incident = new Incident();
|
|
1032
|
+
incident._id = INCIDENT_ID.toString();
|
|
1033
|
+
incident.projectId = PROJECT_ID;
|
|
1034
|
+
incident.title = "Checkout is failing";
|
|
1035
|
+
incident.createdByUserId = USER_ID;
|
|
1036
|
+
incident.declaredAt = OneUptimeDate.getCurrentDate();
|
|
1037
|
+
|
|
1038
|
+
return callHook<Incident>(
|
|
1039
|
+
IncidentService,
|
|
1040
|
+
"onCreateSuccess",
|
|
1041
|
+
{
|
|
1042
|
+
createBy: { data: incident, props: props },
|
|
1043
|
+
carryForward: {
|
|
1044
|
+
alertIdsToLink: [new ObjectID(ALERT_ID), new ObjectID(ALERT_ID_2)],
|
|
1045
|
+
},
|
|
1046
|
+
},
|
|
1047
|
+
incident,
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
test("every alert is linked, and the incident gets exactly one entry and one post", async () => {
|
|
1052
|
+
await declare(userProps(Permission.ProjectMember));
|
|
1053
|
+
|
|
1054
|
+
// Both links were written before the incident was returned.
|
|
1055
|
+
expect(save).toHaveBeenCalledTimes(2);
|
|
1056
|
+
expect(
|
|
1057
|
+
save.mock.calls.map((call: Array<IncidentAlert>) => {
|
|
1058
|
+
return call[0]!.createdByUserId?.toString();
|
|
1059
|
+
}),
|
|
1060
|
+
).toEqual([USER_ID.toString(), USER_ID.toString()]);
|
|
1061
|
+
|
|
1062
|
+
await settle();
|
|
1063
|
+
|
|
1064
|
+
// No per-link "Alert Linked" entry or post: just the one summary.
|
|
1065
|
+
expect(incidentFeed).toHaveBeenCalledTimes(1);
|
|
1066
|
+
|
|
1067
|
+
const entry: Record<string, unknown> = incidentFeed.mock.calls[0]![0];
|
|
1068
|
+
|
|
1069
|
+
expect(entry["incidentFeedEventType"]).toBe(
|
|
1070
|
+
IncidentFeedEventType.AlertLinked,
|
|
1071
|
+
);
|
|
1072
|
+
expect(entry["workspaceNotification"]).toEqual({
|
|
1073
|
+
sendWorkspaceNotification: true,
|
|
1074
|
+
notifyUserId: USER_ID,
|
|
1075
|
+
});
|
|
1076
|
+
expect(entry["feedInfoInMarkdown"]).toBe(
|
|
1077
|
+
[
|
|
1078
|
+
"🔗 Declared from 2 alerts:",
|
|
1079
|
+
"",
|
|
1080
|
+
`- **[Alert #3](https://oneuptime.example/alerts/${ALERT_ID})**: Checkout p95 latency is high`,
|
|
1081
|
+
`- **[Alert #4](https://oneuptime.example/alerts/${ALERT_ID_2})** (private alert)`,
|
|
1082
|
+
].join("\n"),
|
|
1083
|
+
);
|
|
1084
|
+
|
|
1085
|
+
// Each alert still records that it was linked, without a post.
|
|
1086
|
+
expect(alertFeed).toHaveBeenCalledTimes(2);
|
|
1087
|
+
for (const call of alertFeed.mock.calls) {
|
|
1088
|
+
const alertEntry: Record<string, unknown> = call[0];
|
|
1089
|
+
expect(alertEntry["alertFeedEventType"]).toBe(
|
|
1090
|
+
AlertFeedEventType.LinkedToIncident,
|
|
1091
|
+
);
|
|
1092
|
+
expect(alertEntry["workspaceNotification"]).toBeUndefined();
|
|
1093
|
+
expect(String(alertEntry["userId"])).toBe(USER_ID.toString());
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
test("links made one by one outside a declaration each get their own incident entry", async () => {
|
|
1098
|
+
await IncidentAlertService.linkAlertsToIncident({
|
|
1099
|
+
projectId: PROJECT_ID,
|
|
1100
|
+
incidentId: INCIDENT_ID,
|
|
1101
|
+
alertIds: [new ObjectID(ALERT_ID), new ObjectID(ALERT_ID_2)],
|
|
1102
|
+
createdByUserId: USER_ID,
|
|
1103
|
+
props: { isRoot: true },
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
expect(incidentFeed).toHaveBeenCalledTimes(2);
|
|
1107
|
+
expect(alertFeed).toHaveBeenCalledTimes(2);
|
|
1108
|
+
expect(
|
|
1109
|
+
incidentFeed.mock.calls.map((call: Array<Record<string, unknown>>) => {
|
|
1110
|
+
return (call[0]!["workspaceNotification"] as Record<string, unknown>)[
|
|
1111
|
+
"sendWorkspaceNotification"
|
|
1112
|
+
];
|
|
1113
|
+
}),
|
|
1114
|
+
).toEqual([true, true]);
|
|
1115
|
+
});
|
|
1116
|
+
});
|
|
1117
|
+
|
|
1118
|
+
describe("IncidentStateTimelineService.onCreateSuccess hands the new state to the cascade", () => {
|
|
1119
|
+
let cascade: jest.SpyInstance;
|
|
1120
|
+
let release: jest.SpyInstance;
|
|
1121
|
+
let updateIncident: jest.SpyInstance;
|
|
1122
|
+
let stateFeed: jest.SpyInstance;
|
|
1123
|
+
|
|
1124
|
+
beforeEach(() => {
|
|
1125
|
+
const state: IncidentState = new IncidentState();
|
|
1126
|
+
state._id = ACKNOWLEDGED_STATE_ID;
|
|
1127
|
+
state.name = "Acknowledged";
|
|
1128
|
+
state.isAcknowledgedState = true;
|
|
1129
|
+
state.isResolvedState = false;
|
|
1130
|
+
|
|
1131
|
+
jest
|
|
1132
|
+
.spyOn(IncidentStateService, "findOneBy")
|
|
1133
|
+
.mockResolvedValue(state as never);
|
|
1134
|
+
updateIncident = jest
|
|
1135
|
+
.spyOn(IncidentService, "updateOneBy")
|
|
1136
|
+
.mockResolvedValue(1 as never);
|
|
1137
|
+
jest.spyOn(IncidentService, "getIncidentNumber").mockResolvedValue({
|
|
1138
|
+
number: 17,
|
|
1139
|
+
numberWithPrefix: "INC-17",
|
|
1140
|
+
} as never);
|
|
1141
|
+
jest
|
|
1142
|
+
.spyOn(IncidentService, "getIncidentLinkInDashboard")
|
|
1143
|
+
.mockResolvedValue(
|
|
1144
|
+
URL.fromString("https://oneuptime.example/incident") as never,
|
|
1145
|
+
);
|
|
1146
|
+
jest
|
|
1147
|
+
.spyOn(IncidentService, "refreshIncidentMetrics")
|
|
1148
|
+
.mockResolvedValue(undefined as never);
|
|
1149
|
+
jest
|
|
1150
|
+
.spyOn(IncidentMeasurementValueService, "recomputeForIncident")
|
|
1151
|
+
.mockResolvedValue(undefined as never);
|
|
1152
|
+
stateFeed = jest
|
|
1153
|
+
.spyOn(IncidentFeedService, "createIncidentFeedItem")
|
|
1154
|
+
.mockResolvedValue(undefined as never);
|
|
1155
|
+
|
|
1156
|
+
const service: Record<string, () => Promise<unknown>> =
|
|
1157
|
+
IncidentStateTimelineService as unknown as Record<
|
|
1158
|
+
string,
|
|
1159
|
+
() => Promise<unknown>
|
|
1160
|
+
>;
|
|
1161
|
+
jest
|
|
1162
|
+
.spyOn(service, "trackSlaStateChange")
|
|
1163
|
+
.mockResolvedValue(undefined as never);
|
|
1164
|
+
jest
|
|
1165
|
+
.spyOn(service, "isLastIncidentState")
|
|
1166
|
+
.mockResolvedValue(false as never);
|
|
1167
|
+
|
|
1168
|
+
release = jest
|
|
1169
|
+
.spyOn(Semaphore, "release")
|
|
1170
|
+
.mockResolvedValue(undefined as never);
|
|
1171
|
+
|
|
1172
|
+
cascade = jest
|
|
1173
|
+
.spyOn(IncidentAlertService, "cascadeIncidentStateToLinkedAlerts")
|
|
1174
|
+
.mockResolvedValue(undefined as never);
|
|
1175
|
+
});
|
|
1176
|
+
|
|
1177
|
+
function timeline(endsAt?: Date): IncidentStateTimeline {
|
|
1178
|
+
const row: IncidentStateTimeline = new IncidentStateTimeline();
|
|
1179
|
+
row._id = ObjectID.generate().toString();
|
|
1180
|
+
row.projectId = PROJECT_ID;
|
|
1181
|
+
row.incidentId = INCIDENT_ID;
|
|
1182
|
+
row.incidentStateId = new ObjectID(ACKNOWLEDGED_STATE_ID);
|
|
1183
|
+
row.startsAt = OneUptimeDate.getCurrentDate();
|
|
1184
|
+
if (endsAt) {
|
|
1185
|
+
row.endsAt = endsAt;
|
|
1186
|
+
}
|
|
1187
|
+
return row;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
function onCreateSuccess(
|
|
1191
|
+
createdItem: IncidentStateTimeline,
|
|
1192
|
+
mutex: SemaphoreMutex | null = null,
|
|
1193
|
+
): Promise<IncidentStateTimeline> {
|
|
1194
|
+
return callHook<IncidentStateTimeline>(
|
|
1195
|
+
IncidentStateTimelineService,
|
|
1196
|
+
"onCreateSuccess",
|
|
1197
|
+
{
|
|
1198
|
+
createBy: { data: createdItem, props: { isRoot: true } },
|
|
1199
|
+
carryForward: {
|
|
1200
|
+
statusTimelineBeforeThisStatus: null,
|
|
1201
|
+
statusTimelineAfterThisStatus: null,
|
|
1202
|
+
publicNote: undefined,
|
|
1203
|
+
mutex: mutex,
|
|
1204
|
+
},
|
|
1205
|
+
},
|
|
1206
|
+
createdItem,
|
|
1207
|
+
);
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
test("the incident's new current state is cascaded to its linked alerts", async () => {
|
|
1211
|
+
await onCreateSuccess(timeline());
|
|
1212
|
+
|
|
1213
|
+
expect(cascade).toHaveBeenCalledTimes(1);
|
|
1214
|
+
|
|
1215
|
+
const args: {
|
|
1216
|
+
projectId: ObjectID;
|
|
1217
|
+
incidentId: ObjectID;
|
|
1218
|
+
incidentStateId: ObjectID;
|
|
1219
|
+
} = cascade.mock.calls[0]![0];
|
|
1220
|
+
|
|
1221
|
+
expect(args.projectId).toBe(PROJECT_ID);
|
|
1222
|
+
expect(args.incidentId).toBe(INCIDENT_ID);
|
|
1223
|
+
expect(args.incidentStateId.toString()).toBe(ACKNOWLEDGED_STATE_ID);
|
|
1224
|
+
});
|
|
1225
|
+
|
|
1226
|
+
test("the cascade starts only after the incident's mutex is released", async () => {
|
|
1227
|
+
const mutex: SemaphoreMutex = {
|
|
1228
|
+
key: INCIDENT_ID.toString(),
|
|
1229
|
+
} as unknown as SemaphoreMutex;
|
|
1230
|
+
|
|
1231
|
+
await onCreateSuccess(timeline(), mutex);
|
|
1232
|
+
|
|
1233
|
+
expect(release).toHaveBeenCalledWith(mutex);
|
|
1234
|
+
expect(release.mock.invocationCallOrder[0]!).toBeLessThan(
|
|
1235
|
+
cascade.mock.invocationCallOrder[0]!,
|
|
1236
|
+
);
|
|
1237
|
+
});
|
|
1238
|
+
|
|
1239
|
+
test("a back-dated row (one with an end) is not the current state and cascades nothing", async () => {
|
|
1240
|
+
await onCreateSuccess(timeline(OneUptimeDate.getCurrentDate()));
|
|
1241
|
+
|
|
1242
|
+
expect(cascade).not.toHaveBeenCalled();
|
|
1243
|
+
// ...and, as before, it does not become the incident's current state.
|
|
1244
|
+
expect(updateIncident).not.toHaveBeenCalled();
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
test("the state change neither waits for nor fails with the cascade", async () => {
|
|
1248
|
+
let finished: boolean = false;
|
|
1249
|
+
|
|
1250
|
+
cascade.mockImplementation((async (): Promise<void> => {
|
|
1251
|
+
await new Promise<void>((resolve: () => void) => {
|
|
1252
|
+
setTimeout(resolve, 20);
|
|
1253
|
+
});
|
|
1254
|
+
finished = true;
|
|
1255
|
+
throw new Error("cascade failed");
|
|
1256
|
+
}) as never);
|
|
1257
|
+
|
|
1258
|
+
const row: IncidentStateTimeline = timeline();
|
|
1259
|
+
|
|
1260
|
+
await expect(onCreateSuccess(row)).resolves.toBe(row);
|
|
1261
|
+
expect(finished).toBe(false);
|
|
1262
|
+
|
|
1263
|
+
await new Promise<void>((resolve: () => void) => {
|
|
1264
|
+
setTimeout(resolve, 40);
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1267
|
+
expect(finished).toBe(true);
|
|
1268
|
+
expect(errorLog).toHaveBeenCalledWith(
|
|
1269
|
+
expect.stringContaining("carrying the incident state over"),
|
|
1270
|
+
expect.anything(),
|
|
1271
|
+
);
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
test("the rest of the hook is unchanged: the state is stored and announced", async () => {
|
|
1275
|
+
await onCreateSuccess(timeline());
|
|
1276
|
+
|
|
1277
|
+
expect(updateIncident).toHaveBeenCalledWith(
|
|
1278
|
+
expect.objectContaining({
|
|
1279
|
+
data: { currentIncidentStateId: new ObjectID(ACKNOWLEDGED_STATE_ID) },
|
|
1280
|
+
}),
|
|
1281
|
+
);
|
|
1282
|
+
expect(stateFeed).toHaveBeenCalledTimes(1);
|
|
1283
|
+
});
|
|
1284
|
+
});
|