@oneuptime/common 12.0.5 → 12.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/AIRun.ts +33 -0
- package/Models/DatabaseModels/DataSource.ts +744 -0
- package/Models/DatabaseModels/Index.ts +3 -0
- package/Models/DatabaseModels/ProjectCallSMSConfig.ts +10 -2
- package/Models/DatabaseModels/User.ts +2 -0
- package/Models/DatabaseModels/UserOnCallLog.ts +2 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +2 -0
- package/Models/DatabaseModels/UserSession.ts +2 -0
- package/Models/DatabaseModels/UserTotpAuth.ts +6 -4
- package/Models/DatabaseModels/UserWebAuthn.ts +5 -3
- package/Server/API/AIInsightAPI.ts +47 -4
- package/Server/API/AIInvestigationAPI.ts +25 -0
- package/Server/API/DashboardAPI.ts +176 -492
- package/Server/API/DataSourceAPI.ts +268 -0
- package/Server/API/StatusPageAPI.ts +11 -1
- package/Server/API/UserAPI.ts +119 -0
- package/Server/Infrastructure/ClickhouseConfig.ts +59 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.ts +39 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.ts +24 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.ts +76 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Infrastructure/Queue.ts +26 -0
- package/Server/Infrastructure/QueueWorker.ts +17 -0
- package/Server/Middleware/TelemetryIngest.ts +8 -3
- package/Server/Services/AIInsightService.ts +132 -12
- package/Server/Services/AIRunService.ts +34 -0
- package/Server/Services/AIService.ts +17 -2
- package/Server/Services/AlertService.ts +38 -32
- package/Server/Services/AnalyticsDatabaseService.ts +8 -4
- package/Server/Services/DataSourceService.ts +200 -0
- package/Server/Services/HostService.ts +92 -0
- package/Server/Services/IncidentService.ts +45 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/MonitorService.ts +49 -0
- package/Server/Services/MonitorStatusService.ts +166 -0
- package/Server/Services/OpenTelemetryIngestService.ts +255 -184
- package/Server/Services/RumSessionPinService.ts +7 -21
- package/Server/Services/StatusPageSubscriberService.ts +13 -0
- package/Server/Services/UserService.ts +280 -9
- package/Server/Static/Vendor/README.md +62 -0
- package/Server/Static/Vendor/fonts/InterVariable.woff2 +0 -0
- package/Server/Static/Vendor/highlight/highlight.min.js +1244 -0
- package/Server/Static/Vendor/highlight/languages/apache.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/bash.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/c.min.js +41 -0
- package/Server/Static/Vendor/highlight/languages/cpp.min.js +47 -0
- package/Server/Static/Vendor/highlight/languages/csharp.min.js +49 -0
- package/Server/Static/Vendor/highlight/languages/css.min.js +31 -0
- package/Server/Static/Vendor/highlight/languages/diff.min.js +9 -0
- package/Server/Static/Vendor/highlight/languages/dns.min.js +11 -0
- package/Server/Static/Vendor/highlight/languages/dockerfile.min.js +8 -0
- package/Server/Static/Vendor/highlight/languages/go.min.js +20 -0
- package/Server/Static/Vendor/highlight/languages/graphql.min.js +12 -0
- package/Server/Static/Vendor/highlight/languages/http.min.js +14 -0
- package/Server/Static/Vendor/highlight/languages/ini.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/java.min.js +38 -0
- package/Server/Static/Vendor/highlight/languages/javascript.min.js +81 -0
- package/Server/Static/Vendor/highlight/languages/json.min.js +8 -0
- package/Server/Static/Vendor/highlight/languages/kotlin.min.js +46 -0
- package/Server/Static/Vendor/highlight/languages/lua.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/makefile.min.js +14 -0
- package/Server/Static/Vendor/highlight/languages/nginx.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/perl.min.js +41 -0
- package/Server/Static/Vendor/highlight/languages/php.min.js +59 -0
- package/Server/Static/Vendor/highlight/languages/powershell.min.js +39 -0
- package/Server/Static/Vendor/highlight/languages/python.min.js +42 -0
- package/Server/Static/Vendor/highlight/languages/r.min.js +26 -0
- package/Server/Static/Vendor/highlight/languages/ruby.min.js +54 -0
- package/Server/Static/Vendor/highlight/languages/rust.min.js +27 -0
- package/Server/Static/Vendor/highlight/languages/scala.min.js +28 -0
- package/Server/Static/Vendor/highlight/languages/sql.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/swift.min.js +68 -0
- package/Server/Static/Vendor/highlight/languages/typescript.min.js +99 -0
- package/Server/Static/Vendor/highlight/languages/xml.min.js +29 -0
- package/Server/Static/Vendor/highlight/languages/yaml.min.js +28 -0
- package/Server/Static/Vendor/highlight/styles/github-dark.min.css +10 -0
- package/Server/Static/Vendor/highlight/styles/vs2015.min.css +1 -0
- package/Server/Static/Vendor/tailwind/tailwind-3.4.5.js +21028 -0
- package/Server/Types/Database/Permissions/BasePermission.ts +7 -0
- package/Server/Types/Database/Permissions/DeletePermission.ts +2 -0
- package/Server/Types/Database/Permissions/ReadPermission.ts +3 -0
- package/Server/Types/Database/Permissions/TenantPermission.ts +101 -16
- package/Server/Utils/AI/CodeFix/FixRunBudget.ts +2 -2
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +90 -11
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +1 -0
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +1 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.ts +125 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +25 -15
- package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +256 -57
- package/Server/Utils/AI/SRE/Insights/InsightStore.ts +76 -18
- package/Server/Utils/AI/SRE/InvestigationTldr.ts +352 -0
- package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +7 -2
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +1793 -0
- package/Server/Utils/Dashboard/PublicDashboardViewConfig.ts +65 -0
- package/Server/Utils/DataSource/ConnectorRegistry.ts +53 -0
- package/Server/Utils/DataSource/Connectors/ClickHouseConnector.ts +338 -0
- package/Server/Utils/DataSource/Connectors/ElasticsearchConnector.ts +717 -0
- package/Server/Utils/DataSource/Connectors/LokiConnector.ts +606 -0
- package/Server/Utils/DataSource/Connectors/MySqlConnector.ts +307 -0
- package/Server/Utils/DataSource/Connectors/PostgresConnector.ts +260 -0
- package/Server/Utils/DataSource/Connectors/PrometheusConnector.ts +555 -0
- package/Server/Utils/DataSource/Connectors/RestApiConnector.ts +597 -0
- package/Server/Utils/DataSource/Connectors/SqlServerConnector.ts +318 -0
- package/Server/Utils/DataSource/EgressGuard.ts +446 -0
- package/Server/Utils/DataSource/HttpFetch.ts +221 -0
- package/Server/Utils/DataSource/SqlQueryGuard.ts +383 -0
- package/Server/Utils/DataSource/SqlResultShaper.ts +372 -0
- package/Server/Utils/DataSource/Types.ts +93 -0
- package/Server/Utils/Database/PostgresErrorTranslator.ts +172 -17
- package/Server/Utils/InProcessMemo.ts +141 -0
- package/Server/Utils/InstanceHealth/TelemetryIngestion.ts +628 -0
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +58 -0
- package/Server/Utils/Monitor/MonitorResource.ts +14 -0
- package/Server/Utils/Response.ts +12 -1
- package/Server/Utils/SSRFProtection.ts +237 -0
- package/Server/Utils/StartServer.ts +8 -0
- package/Server/Utils/StatusPageSubscriberWebhook.ts +30 -0
- package/Server/Utils/Telemetry/CaptureSpan.ts +30 -18
- package/Server/Utils/Telemetry/IngestionTimestamp.ts +75 -0
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +99 -7
- package/Server/Utils/Telemetry/Telemetry.ts +112 -1
- package/Server/Utils/Telemetry.ts +104 -3
- package/Server/Utils/VendorAssets.ts +165 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +28 -5
- package/Server/Utils/Workspace/Slack/Slack.ts +5 -0
- package/Server/Views/Partials/Head.ejs +21 -7
- package/Tests/App/AdminDashboard/AdminProjectsIdFilter.test.tsx +207 -0
- package/Tests/App/AdminDashboard/AdminUsersIdFilter.test.tsx +216 -0
- package/Tests/App/Dashboard/AIInsightViewActions.test.tsx +422 -0
- package/Tests/App/Dashboard/AddWidgetModal.test.tsx +10 -3
- package/Tests/App/Dashboard/AlertsSideMenu.test.tsx +387 -0
- package/Tests/App/Dashboard/ChatActivityFeed.test.tsx +199 -0
- package/Tests/App/Dashboard/DataSourceChartSyntheticConfig.test.tsx +736 -0
- package/Tests/App/Dashboard/DataSourceQueryEditor.test.tsx +868 -0
- package/Tests/App/Dashboard/DataSourceWidgetCatalog.test.ts +440 -0
- package/Tests/App/Dashboard/DataSourceWidgetFetching.test.tsx +2109 -0
- package/Tests/App/Dashboard/DataSourceWidgetViews.test.tsx +726 -0
- package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +447 -0
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +514 -3
- package/Tests/App/Dashboard/ProductMenuBreadcrumbs.test.tsx +391 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceSideMenu.test.tsx +326 -0
- package/Tests/App/Dashboard/SideMenuHarness.tsx +187 -0
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +6 -1
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +1858 -0
- package/Tests/Models/DatabaseModels/DataSourceModelSecrets.test.ts +70 -0
- package/Tests/Server/API/AIInvestigationAPI.test.ts +187 -0
- package/Tests/Server/API/DashboardPublicDataSourceStrip.test.ts +686 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +585 -823
- package/Tests/Server/API/DataSourceAPIRoutes.test.ts +486 -0
- package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +291 -0
- package/Tests/Server/API/UserAuthenticationAPI.test.ts +641 -0
- package/Tests/Server/Infrastructure/ClickhouseKeepAliveOptions.test.ts +441 -0
- package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +140 -0
- package/Tests/Server/Services/AIInsightService.test.ts +266 -11
- package/Tests/Server/Services/AIRunAnalysisTldrPersistence.test.ts +160 -0
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +4 -0
- package/Tests/Server/Services/AlertMetricResourceAttributes.test.ts +352 -0
- package/Tests/Server/Services/DataSourceServiceSettings.test.ts +244 -0
- package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +397 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +41 -2
- package/Tests/Server/Services/IncidentMetricResourceAttributes.test.ts +408 -0
- package/Tests/Server/Services/MonitorStatusServiceDeleteReferenceCleanup.test.ts +351 -0
- package/Tests/Server/Services/MonitorStatusServiceValidation.test.ts +171 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +398 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +19 -0
- package/Tests/Server/Services/ProjectCallSMSConfigCredentialUniqueness.test.ts +305 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +36 -1
- package/Tests/Server/Services/RumSessionPinUniqueViolationRecovery.test.ts +163 -0
- package/Tests/Server/Services/UserAuthenticationService.test.ts +973 -0
- package/Tests/Server/Types/Database/ObjectIdColumnQuery.test.ts +262 -0
- package/Tests/Server/Types/Database/Permissions/TenantPermission.test.ts +358 -0
- package/Tests/Server/Utils/AI/FixRunBudget.test.ts +2 -2
- package/Tests/Server/Utils/AI/Insights/ExceptionSpikeDetector.test.ts +6 -1
- package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +121 -4
- package/Tests/Server/Utils/AI/Insights/NewExceptionDetector.test.ts +361 -10
- package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +292 -0
- package/Tests/Server/Utils/AI/InvestigationTldr.test.ts +623 -0
- package/Tests/Server/Utils/AI/InvestigationTldrWiring.test.ts +98 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +857 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardViewConfig.test.ts +480 -0
- package/Tests/Server/Utils/DataSource/ClickHouseConnector.test.ts +759 -0
- package/Tests/Server/Utils/DataSource/ConnectorRegistry.test.ts +37 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +645 -0
- package/Tests/Server/Utils/DataSource/ElasticsearchConnector.test.ts +1206 -0
- package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +616 -0
- package/Tests/Server/Utils/DataSource/LokiConnector.test.ts +970 -0
- package/Tests/Server/Utils/DataSource/MySqlConnector.test.ts +935 -0
- package/Tests/Server/Utils/DataSource/PostgresConnector.test.ts +854 -0
- package/Tests/Server/Utils/DataSource/PrometheusConnector.test.ts +881 -0
- package/Tests/Server/Utils/DataSource/RestApiConnector.test.ts +1020 -0
- package/Tests/Server/Utils/DataSource/SqlQueryGuard.test.ts +425 -0
- package/Tests/Server/Utils/DataSource/SqlResultShaper.test.ts +555 -0
- package/Tests/Server/Utils/DataSource/SqlServerConnector.test.ts +934 -0
- package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +228 -7
- package/Tests/Server/Utils/InProcessMemo.test.ts +292 -0
- package/Tests/Server/Utils/InstanceHealth/TelemetryIngestion.test.ts +960 -0
- package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +103 -0
- package/Tests/Server/Utils/Monitor/MonitorMetricResourceAttributes.test.ts +336 -0
- package/Tests/Server/Utils/OfflineAssetHygiene.test.ts +521 -0
- package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +143 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +193 -0
- package/Tests/Server/Utils/StatusPageSubscriberWebhook.test.ts +101 -0
- package/Tests/Server/Utils/TailwindBuildCopy.test.ts +225 -0
- package/Tests/Server/Utils/Telemetry/ResourceHeartbeatL1Memo.test.ts +355 -0
- package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +128 -0
- package/Tests/Server/Utils/Telemetry/TelemetryUtilAttributeKeys.test.ts +381 -0
- package/Tests/Server/Utils/Telemetry.test.ts +87 -0
- package/Tests/Server/Utils/VendorAssetIntegrity.test.ts +343 -0
- package/Tests/Server/Utils/VendorAssets.test.ts +671 -0
- package/Tests/Server/Views/OfflineRender.test.ts +297 -0
- package/Tests/Types/Dashboard/DataSourceComponentTypes.test.ts +618 -0
- package/Tests/Types/DateClickhouseFormat.test.ts +192 -0
- package/Tests/Types/Password.test.ts +363 -0
- package/Tests/UI/Components/ConfirmModal.test.tsx +7 -0
- package/Tests/UI/Components/ErrorBoundary.test.tsx +53 -0
- package/Tests/UI/Components/Modal.test.tsx +281 -1
- package/Tests/UI/Components/ObjectIdFilterPipeline.test.tsx +396 -0
- package/Tests/UI/Components/StatusPage/ResourceGroupNavigator.test.tsx +1147 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +116 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
- package/Tests/Utils/Analytics.test.ts +23 -0
- package/Tests/Utils/Dashboard/Components/DashboardDataSourceComponents.test.ts +640 -0
- package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +138 -0
- package/Tests/Utils/DataSource/DataSourceValueReducer.test.ts +526 -0
- package/Tests/Utils/Metrics/MetricResourceAttributeUtil.test.ts +691 -0
- package/Tests/Utils/StatusPage/GroupHierarchyView.test.ts +1452 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +931 -0
- package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +288 -0
- package/Tests/Utils/Telemetry/IngestionTimestamp.test.ts +170 -0
- package/Types/AI/AIInsightEvidence.ts +6 -0
- package/Types/Analytics/RevenueEvent.ts +31 -0
- package/Types/Dashboard/DashboardComponentType.ts +31 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.ts +27 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.ts +32 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.ts +30 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.ts +55 -0
- package/Types/DataSource/DataSourceLimits.ts +62 -0
- package/Types/DataSource/DataSourceQueryConfig.ts +41 -0
- package/Types/DataSource/DataSourceTableResult.ts +38 -0
- package/Types/DataSource/DataSourceType.ts +213 -0
- package/Types/Date.ts +43 -3
- package/Types/Password.ts +64 -0
- package/Types/Permission.ts +46 -0
- package/Types/UserAuthenticationStatus.ts +34 -0
- package/UI/Components/Button/Button.tsx +1 -1
- package/UI/Components/ErrorBoundary.tsx +459 -108
- package/UI/Components/Icon/Icon.tsx +10 -9
- package/UI/Components/Modal/ConfirmModal.tsx +6 -1
- package/UI/Components/Modal/Modal.tsx +267 -35
- package/UI/Components/Modal/ModalFooter.tsx +6 -1
- package/UI/Components/MoreMenu/MoreMenu.tsx +19 -4
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +6 -1
- package/UI/Components/StatusPage/ResourceGrid.tsx +314 -0
- package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +808 -0
- package/UI/Components/StatusPage/ResourceList.tsx +570 -0
- package/UI/Styles/Theme.css +18 -0
- package/UI/Utils/API/RequestOptions.ts +7 -0
- package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +1 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +1 -0
- package/UI/Utils/TestDataSource.ts +61 -0
- package/UI/esbuild-config.js +45 -0
- package/Utils/Analytics.ts +15 -0
- package/Utils/Dashboard/Components/DashboardDataSourceChartComponent.ts +88 -0
- package/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.ts +123 -0
- package/Utils/Dashboard/Components/DashboardDataSourceShared.ts +16 -0
- package/Utils/Dashboard/Components/DashboardDataSourceTableComponent.ts +72 -0
- package/Utils/Dashboard/Components/DashboardDataSourceValueComponent.ts +126 -0
- package/Utils/Dashboard/Components/Index.ts +28 -0
- package/Utils/DataSource/DataSourceQueryText.ts +94 -0
- package/Utils/DataSource/DataSourceValueReducer.ts +156 -0
- package/Utils/Metrics/MetricResourceAttributeUtil.ts +380 -0
- package/Utils/NetworkDiscovery/ScanTargetUtil.ts +8 -6
- package/Utils/StatusPage/GroupHierarchyView.ts +931 -0
- package/Utils/StatusPage/ResourceExplorer.ts +853 -0
- package/build/dist/Models/DatabaseModels/AIRun.js +34 -0
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DataSource.js +774 -0
- package/build/dist/Models/DatabaseModels/DataSource.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js +14 -4
- package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +2 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js +2 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +2 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSession.js +2 -0
- package/build/dist/Models/DatabaseModels/UserSession.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserTotpAuth.js +6 -4
- package/build/dist/Models/DatabaseModels/UserTotpAuth.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js +5 -3
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -1
- package/build/dist/Server/API/AIInsightAPI.js +34 -4
- package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +23 -0
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +119 -433
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/DataSourceAPI.js +173 -0
- package/build/dist/Server/API/DataSourceAPI.js.map +1 -0
- package/build/dist/Server/API/StatusPageAPI.js +11 -1
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +94 -2
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js +49 -0
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js +64 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +27 -0
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Infrastructure/QueueWorker.js +13 -0
- package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +8 -3
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AIInsightService.js +110 -10
- package/build/dist/Server/Services/AIInsightService.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +34 -0
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +16 -2
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +28 -35
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +8 -4
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DataSourceService.js +159 -0
- package/build/dist/Server/Services/DataSourceService.js.map +1 -0
- package/build/dist/Server/Services/HostService.js +74 -0
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +35 -24
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +45 -0
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusService.js +129 -0
- package/build/dist/Server/Services/MonitorStatusService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +222 -159
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionPinService.js +7 -15
- package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +10 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +241 -10
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +6 -0
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/DeletePermission.js +1 -0
- package/build/dist/Server/Types/Database/Permissions/DeletePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +2 -0
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +68 -12
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +2 -2
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +68 -8
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +1 -0
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +1 -0
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js +97 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +17 -15
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +178 -54
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js +65 -16
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js +303 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js.map +1 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +1261 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js +47 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js +42 -0
- package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js +187 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js +493 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js +399 -0
- package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js +195 -0
- package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js +149 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js +362 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js +342 -0
- package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js +186 -0
- package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +305 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/HttpFetch.js +134 -0
- package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js +331 -0
- package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/SqlResultShaper.js +280 -0
- package/build/dist/Server/Utils/DataSource/SqlResultShaper.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Types.js +2 -0
- package/build/dist/Server/Utils/DataSource/Types.js.map +1 -0
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +103 -9
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -1
- package/build/dist/Server/Utils/InProcessMemo.js +113 -0
- package/build/dist/Server/Utils/InProcessMemo.js.map +1 -0
- package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js +406 -0
- package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +34 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +14 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Response.js +9 -1
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +192 -0
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -0
- package/build/dist/Server/Utils/StartServer.js +7 -0
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js +24 -0
- package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +25 -11
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js +32 -0
- package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +92 -5
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +92 -6
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry.js +93 -6
- package/build/dist/Server/Utils/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/VendorAssets.js +121 -0
- package/build/dist/Server/Utils/VendorAssets.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -4
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +5 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Types/Analytics/RevenueEvent.js +26 -0
- package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js +27 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js +16 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceLimits.js +47 -0
- package/build/dist/Types/DataSource/DataSourceLimits.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceQueryConfig.js +10 -0
- package/build/dist/Types/DataSource/DataSourceQueryConfig.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceTableResult.js +14 -0
- package/build/dist/Types/DataSource/DataSourceTableResult.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceType.js +183 -0
- package/build/dist/Types/DataSource/DataSourceType.js.map +1 -0
- package/build/dist/Types/Date.js +38 -3
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Password.js +56 -0
- package/build/dist/Types/Password.js.map +1 -0
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/UserAuthenticationStatus.js +2 -0
- package/build/dist/Types/UserAuthenticationStatus.js.map +1 -0
- package/build/dist/UI/Components/ErrorBoundary.js +388 -72
- package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +10 -4
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +178 -19
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js +3 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +9 -6
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +7 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +119 -0
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +354 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -0
- package/build/dist/UI/Components/StatusPage/ResourceList.js +203 -0
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -0
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +1 -6
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +3 -8
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/TestDataSource.js +39 -0
- package/build/dist/UI/Utils/TestDataSource.js.map +1 -0
- package/build/dist/Utils/Analytics.js +4 -0
- package/build/dist/Utils/Analytics.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js +79 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js +111 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js +15 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js +62 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js +112 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +16 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/DataSource/DataSourceQueryText.js +64 -0
- package/build/dist/Utils/DataSource/DataSourceQueryText.js.map +1 -0
- package/build/dist/Utils/DataSource/DataSourceValueReducer.js +124 -0
- package/build/dist/Utils/DataSource/DataSourceValueReducer.js.map +1 -0
- package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js +281 -0
- package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +8 -6
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
- package/build/dist/Utils/StatusPage/GroupHierarchyView.js +560 -0
- package/build/dist/Utils/StatusPage/GroupHierarchyView.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js +519 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -0
- package/jest.config.json +1 -0
- package/package.json +5 -1
- package/tsconfig.json +12 -10
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import InsightStore, {
|
|
2
2
|
DISMISSED_COOLDOWN_DAYS,
|
|
3
3
|
MAX_NEW_INSIGHTS_PER_PROJECT_PER_SCAN,
|
|
4
|
+
RESOLVED_COOLDOWN_HOURS,
|
|
4
5
|
UpsertCandidatesResult,
|
|
5
6
|
} from "../../../../../Server/Utils/AI/SRE/Insights/InsightStore";
|
|
6
7
|
import { InsightCandidate } from "../../../../../Server/Utils/AI/SRE/Insights/Types";
|
|
@@ -46,12 +47,14 @@ function fakeExisting(overrides?: {
|
|
|
46
47
|
status?: AIInsightStatus;
|
|
47
48
|
occurrenceCount?: number;
|
|
48
49
|
humanVerdictAt?: Date | undefined;
|
|
50
|
+
lastSeenAt?: Date | undefined;
|
|
49
51
|
}): AIInsight {
|
|
50
52
|
return {
|
|
51
53
|
id: ObjectID.generate(),
|
|
52
54
|
status: overrides?.status ?? AIInsightStatus.Detected,
|
|
53
55
|
occurrenceCount: overrides?.occurrenceCount ?? 2,
|
|
54
56
|
humanVerdictAt: overrides?.humanVerdictAt,
|
|
57
|
+
lastSeenAt: overrides?.lastSeenAt,
|
|
55
58
|
} as unknown as AIInsight;
|
|
56
59
|
}
|
|
57
60
|
|
|
@@ -166,7 +169,7 @@ describe("InsightStore.upsertCandidates — dedupe matrix", () => {
|
|
|
166
169
|
[AIInsightStatus.ActionRequired],
|
|
167
170
|
[AIInsightStatus.FixOpened],
|
|
168
171
|
])(
|
|
169
|
-
"existing %s insight → REFRESH: lastSeenAt, occurrenceCount+1, evidence/detail/severity updated — and status is NEVER touched",
|
|
172
|
+
"existing %s insight → REFRESH: lastSeenAt, occurrenceCount+1, title/evidence/detail/severity updated — and status is NEVER touched",
|
|
170
173
|
async (status: AIInsightStatus) => {
|
|
171
174
|
const existing: AIInsight = fakeExisting({
|
|
172
175
|
status,
|
|
@@ -179,7 +182,17 @@ describe("InsightStore.upsertCandidates — dedupe matrix", () => {
|
|
|
179
182
|
const result: UpsertCandidatesResult =
|
|
180
183
|
await InsightStore.upsertCandidates({
|
|
181
184
|
projectId,
|
|
182
|
-
candidates: [
|
|
185
|
+
candidates: [
|
|
186
|
+
makeCandidate({
|
|
187
|
+
severity: AIInsightSeverity.High,
|
|
188
|
+
/*
|
|
189
|
+
* Detectors put live numbers in the title (a spike multiplier,
|
|
190
|
+
* a latency factor). A title frozen at creation would show
|
|
191
|
+
* "5.2x" next to a High severity and a 12x detail body.
|
|
192
|
+
*/
|
|
193
|
+
title: "New exception: NullPointerException in checkout (12.0x)",
|
|
194
|
+
}),
|
|
195
|
+
],
|
|
183
196
|
now,
|
|
184
197
|
});
|
|
185
198
|
|
|
@@ -195,6 +208,7 @@ describe("InsightStore.upsertCandidates — dedupe matrix", () => {
|
|
|
195
208
|
lastSeenAt: now,
|
|
196
209
|
occurrenceCount: 5,
|
|
197
210
|
severity: AIInsightSeverity.High,
|
|
211
|
+
title: "New exception: NullPointerException in checkout (12.0x)",
|
|
198
212
|
detailMarkdown: "**3 occurrences** in the last 24 hours.",
|
|
199
213
|
evidence: { exception: { recentOccurrenceCount: 3 } },
|
|
200
214
|
}),
|
|
@@ -317,8 +331,65 @@ describe("InsightStore.upsertCandidates — dedupe matrix", () => {
|
|
|
317
331
|
expect(result.suppressed).toBe(1);
|
|
318
332
|
});
|
|
319
333
|
|
|
320
|
-
|
|
321
|
-
|
|
334
|
+
/*
|
|
335
|
+
* Resolve has to stick. Detectors emit on a standing condition, not on an
|
|
336
|
+
* event — NewException re-emits an unchanged candidate on all ~96 ticks of
|
|
337
|
+
* its 24h window — so without a cooldown a resolved insight was re-filed
|
|
338
|
+
* byte-identical 15 minutes later, with its own triage run, every time.
|
|
339
|
+
*/
|
|
340
|
+
test("existing Resolved insight within the cooldown → suppress, not a byte-identical re-file", async () => {
|
|
341
|
+
mockFindOneBy(
|
|
342
|
+
fakeExisting({
|
|
343
|
+
status: AIInsightStatus.Resolved,
|
|
344
|
+
humanVerdictAt: new Date(now.getTime() - 15 * 60 * 1000),
|
|
345
|
+
}),
|
|
346
|
+
);
|
|
347
|
+
const create: jest.SpyInstance = mockCreate();
|
|
348
|
+
const update: jest.SpyInstance = mockUpdateOneById();
|
|
349
|
+
|
|
350
|
+
const result: UpsertCandidatesResult = await InsightStore.upsertCandidates({
|
|
351
|
+
projectId,
|
|
352
|
+
candidates: [makeCandidate()],
|
|
353
|
+
now,
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
expect(create).not.toHaveBeenCalled();
|
|
357
|
+
expect(update).not.toHaveBeenCalled();
|
|
358
|
+
expect(result.suppressed).toBe(1);
|
|
359
|
+
expect(result.created).toHaveLength(0);
|
|
360
|
+
expect(result.refreshed).toBe(0);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test("resolve cooldown boundary: exactly RESOLVED_COOLDOWN_HOURS old still suppresses (inclusive)", async () => {
|
|
364
|
+
mockFindOneBy(
|
|
365
|
+
fakeExisting({
|
|
366
|
+
status: AIInsightStatus.Resolved,
|
|
367
|
+
humanVerdictAt: new Date(
|
|
368
|
+
now.getTime() - RESOLVED_COOLDOWN_HOURS * 60 * 60 * 1000,
|
|
369
|
+
),
|
|
370
|
+
}),
|
|
371
|
+
);
|
|
372
|
+
const create: jest.SpyInstance = mockCreate();
|
|
373
|
+
|
|
374
|
+
const result: UpsertCandidatesResult = await InsightStore.upsertCandidates({
|
|
375
|
+
projectId,
|
|
376
|
+
candidates: [makeCandidate()],
|
|
377
|
+
now,
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
expect(create).not.toHaveBeenCalled();
|
|
381
|
+
expect(result.suppressed).toBe(1);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test("resolved past the cooldown → CREATE new: a resolved issue that comes back IS a regression", async () => {
|
|
385
|
+
mockFindOneBy(
|
|
386
|
+
fakeExisting({
|
|
387
|
+
status: AIInsightStatus.Resolved,
|
|
388
|
+
humanVerdictAt: new Date(
|
|
389
|
+
now.getTime() - (RESOLVED_COOLDOWN_HOURS * 60 * 60 * 1000 + 1),
|
|
390
|
+
),
|
|
391
|
+
}),
|
|
392
|
+
);
|
|
322
393
|
const create: jest.SpyInstance = mockCreate();
|
|
323
394
|
const update: jest.SpyInstance = mockUpdateOneById();
|
|
324
395
|
|
|
@@ -333,6 +404,52 @@ describe("InsightStore.upsertCandidates — dedupe matrix", () => {
|
|
|
333
404
|
expect(result.created).toHaveLength(1);
|
|
334
405
|
expect(result.refreshed).toBe(0);
|
|
335
406
|
});
|
|
407
|
+
|
|
408
|
+
/*
|
|
409
|
+
* resolveInsight leaves an existing verdict alone, so a Confirm-then-Resolve
|
|
410
|
+
* leaves humanVerdictAt pinned to the CONFIRM. lastSeenAt is the honest
|
|
411
|
+
* fallback: refreshes stop the instant a row goes terminal, so it is the
|
|
412
|
+
* last tick before the human closed it — never later than the close.
|
|
413
|
+
*/
|
|
414
|
+
test("no verdict timestamp: the cooldown falls back to lastSeenAt", async () => {
|
|
415
|
+
mockFindOneBy(
|
|
416
|
+
fakeExisting({
|
|
417
|
+
status: AIInsightStatus.Resolved,
|
|
418
|
+
humanVerdictAt: undefined,
|
|
419
|
+
lastSeenAt: new Date(now.getTime() - 60 * 60 * 1000),
|
|
420
|
+
}),
|
|
421
|
+
);
|
|
422
|
+
const create: jest.SpyInstance = mockCreate();
|
|
423
|
+
|
|
424
|
+
const result: UpsertCandidatesResult = await InsightStore.upsertCandidates({
|
|
425
|
+
projectId,
|
|
426
|
+
candidates: [makeCandidate()],
|
|
427
|
+
now,
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
expect(create).not.toHaveBeenCalled();
|
|
431
|
+
expect(result.suppressed).toBe(1);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
test("an undateable terminal row suppresses — never re-file what we cannot date", async () => {
|
|
435
|
+
mockFindOneBy(
|
|
436
|
+
fakeExisting({
|
|
437
|
+
status: AIInsightStatus.Resolved,
|
|
438
|
+
humanVerdictAt: undefined,
|
|
439
|
+
lastSeenAt: undefined,
|
|
440
|
+
}),
|
|
441
|
+
);
|
|
442
|
+
const create: jest.SpyInstance = mockCreate();
|
|
443
|
+
|
|
444
|
+
const result: UpsertCandidatesResult = await InsightStore.upsertCandidates({
|
|
445
|
+
projectId,
|
|
446
|
+
candidates: [makeCandidate()],
|
|
447
|
+
now,
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
expect(create).not.toHaveBeenCalled();
|
|
451
|
+
expect(result.suppressed).toBe(1);
|
|
452
|
+
});
|
|
336
453
|
});
|
|
337
454
|
|
|
338
455
|
describe("InsightStore.upsertCandidates — per-scan new-insight cap", () => {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { afterEach, describe, expect, test } from "@jest/globals";
|
|
2
2
|
import NewExceptionDetector, {
|
|
3
3
|
NEW_EXCEPTION_CANDIDATE_LIMIT,
|
|
4
|
+
NEW_EXCEPTION_GROUP_SCAN_LIMIT,
|
|
4
5
|
NEW_EXCEPTION_HIGH_SEVERITY_OCCURRENCE_COUNT,
|
|
5
6
|
NEW_EXCEPTION_MIN_OCCURRENCE_COUNT,
|
|
6
7
|
NEW_EXCEPTION_TITLE_MESSAGE_MAX_LENGTH,
|
|
7
8
|
NewExceptionDecision,
|
|
8
9
|
} from "../../../../../Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector";
|
|
10
|
+
import { buildExceptionFailureModeKey } from "../../../../../Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity";
|
|
9
11
|
import { InsightCandidate } from "../../../../../Server/Utils/AI/SRE/Insights/Types";
|
|
10
12
|
import TelemetryExceptionService from "../../../../../Server/Services/TelemetryExceptionService";
|
|
11
13
|
import ServiceService from "../../../../../Server/Services/ServiceService";
|
|
@@ -13,15 +15,20 @@ import TelemetryException from "../../../../../Models/DatabaseModels/TelemetryEx
|
|
|
13
15
|
import Service from "../../../../../Models/DatabaseModels/Service";
|
|
14
16
|
import AIInsightSeverity from "../../../../../Types/AI/AIInsightSeverity";
|
|
15
17
|
import AIInsightType from "../../../../../Types/AI/AIInsightType";
|
|
18
|
+
import SortOrder from "../../../../../Types/BaseDatabase/SortOrder";
|
|
16
19
|
import ObjectID from "../../../../../Types/ObjectID";
|
|
17
20
|
|
|
18
21
|
/*
|
|
19
22
|
* Invariant under test: the NewException detector deterministically turns
|
|
20
23
|
* "an exception group born in the last 24h that is already recurring" into
|
|
21
|
-
* exactly one insight candidate per
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
24
|
+
* exactly one insight candidate per FAILURE MODE — (entity, exception type,
|
|
25
|
+
* normalized message), NOT per exception group, because group identity at
|
|
26
|
+
* ingest includes the stack trace and one throw site reached from several
|
|
27
|
+
* call paths is several groups. Occurrences are summed across the member
|
|
28
|
+
* groups, the candidate qualifies at >= 3 and escalates to High at >= 50,
|
|
29
|
+
* the fingerprint is the failure-mode key (never the group id, which would
|
|
30
|
+
* file one insight per call path), and Postgres is read as root with an
|
|
31
|
+
* explicit projectId (never user ACL). No LLM is ever involved.
|
|
25
32
|
*/
|
|
26
33
|
|
|
27
34
|
const projectId: ObjectID = ObjectID.generate();
|
|
@@ -96,10 +103,43 @@ describe("NewExceptionDetector.evaluateNewException (pure decision matrix)", ()
|
|
|
96
103
|
});
|
|
97
104
|
|
|
98
105
|
describe("NewExceptionDetector.buildExceptionLabel (pure)", () => {
|
|
99
|
-
test("
|
|
106
|
+
test("carries the type AND the message — the type alone collides", () => {
|
|
100
107
|
expect(
|
|
101
108
|
NewExceptionDetector.buildExceptionLabel("TypeError", "some message"),
|
|
102
|
-
).toBe("TypeError");
|
|
109
|
+
).toBe("TypeError: some message");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
/*
|
|
113
|
+
* The reported symptom: instrumentation that types every failure by HTTP
|
|
114
|
+
* status turns a whole service's unrelated exceptions into one string.
|
|
115
|
+
*/
|
|
116
|
+
test("a low-signal type is disambiguated by the message", () => {
|
|
117
|
+
expect(
|
|
118
|
+
NewExceptionDetector.buildExceptionLabel(
|
|
119
|
+
"401",
|
|
120
|
+
"Authenticated user or a valid API key is needed to read record of Monitor.",
|
|
121
|
+
),
|
|
122
|
+
).not.toBe(
|
|
123
|
+
NewExceptionDetector.buildExceptionLabel(
|
|
124
|
+
"401",
|
|
125
|
+
"Authenticated user or a valid API key is needed to read record of Incident.",
|
|
126
|
+
),
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("does not repeat a type the runtime already prefixed onto the message", () => {
|
|
131
|
+
expect(
|
|
132
|
+
NewExceptionDetector.buildExceptionLabel("Error", "Error: boom"),
|
|
133
|
+
).toBe("Error: boom");
|
|
134
|
+
expect(
|
|
135
|
+
NewExceptionDetector.buildExceptionLabel("TypeError", "typeerror: boom"),
|
|
136
|
+
).toBe("typeerror: boom");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("falls back to the type alone when there is no message", () => {
|
|
140
|
+
expect(NewExceptionDetector.buildExceptionLabel("TypeError", "")).toBe(
|
|
141
|
+
"TypeError",
|
|
142
|
+
);
|
|
103
143
|
});
|
|
104
144
|
|
|
105
145
|
test("falls back to the message when the type is empty/whitespace", () => {
|
|
@@ -151,6 +191,94 @@ describe("NewExceptionDetector.buildFingerprint (pure)", () => {
|
|
|
151
191
|
});
|
|
152
192
|
});
|
|
153
193
|
|
|
194
|
+
/*
|
|
195
|
+
* The failure-mode key IS the insight's identity. Two properties matter and
|
|
196
|
+
* both are load-bearing: it must ignore the stack trace (so one throw site
|
|
197
|
+
* reached from many call paths is one insight), and it must be stable across
|
|
198
|
+
* ticks and releases (a key that drifts files a duplicate on every drift).
|
|
199
|
+
*/
|
|
200
|
+
describe("buildExceptionFailureModeKey (pure identity)", () => {
|
|
201
|
+
test("same entity + type + message → same key, whatever the stack trace", () => {
|
|
202
|
+
const a: string = buildExceptionFailureModeKey({
|
|
203
|
+
primaryEntityId: serviceId,
|
|
204
|
+
exceptionType: "401",
|
|
205
|
+
message: "Unauthorized",
|
|
206
|
+
});
|
|
207
|
+
const b: string = buildExceptionFailureModeKey({
|
|
208
|
+
primaryEntityId: serviceId,
|
|
209
|
+
exceptionType: "401",
|
|
210
|
+
message: "Unauthorized",
|
|
211
|
+
});
|
|
212
|
+
expect(a).toBe(b);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("interpolated ids do not split one failure mode", () => {
|
|
216
|
+
expect(
|
|
217
|
+
buildExceptionFailureModeKey({
|
|
218
|
+
primaryEntityId: serviceId,
|
|
219
|
+
exceptionType: "BadDataException",
|
|
220
|
+
message:
|
|
221
|
+
"invalid input syntax for type uuid: 550e8400-e29b-41d4-a716-446655440000",
|
|
222
|
+
}),
|
|
223
|
+
).toBe(
|
|
224
|
+
buildExceptionFailureModeKey({
|
|
225
|
+
primaryEntityId: serviceId,
|
|
226
|
+
exceptionType: "BadDataException",
|
|
227
|
+
message:
|
|
228
|
+
"invalid input syntax for type uuid: 6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
229
|
+
}),
|
|
230
|
+
);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("a different message, type or entity is a different failure mode", () => {
|
|
234
|
+
const base: string = buildExceptionFailureModeKey({
|
|
235
|
+
primaryEntityId: serviceId,
|
|
236
|
+
exceptionType: "401",
|
|
237
|
+
message: "read record of Monitor",
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
expect(
|
|
241
|
+
buildExceptionFailureModeKey({
|
|
242
|
+
primaryEntityId: serviceId,
|
|
243
|
+
exceptionType: "401",
|
|
244
|
+
message: "read record of Incident",
|
|
245
|
+
}),
|
|
246
|
+
).not.toBe(base);
|
|
247
|
+
|
|
248
|
+
expect(
|
|
249
|
+
buildExceptionFailureModeKey({
|
|
250
|
+
primaryEntityId: serviceId,
|
|
251
|
+
exceptionType: "403",
|
|
252
|
+
message: "read record of Monitor",
|
|
253
|
+
}),
|
|
254
|
+
).not.toBe(base);
|
|
255
|
+
|
|
256
|
+
expect(
|
|
257
|
+
buildExceptionFailureModeKey({
|
|
258
|
+
primaryEntityId: ObjectID.generate(),
|
|
259
|
+
exceptionType: "401",
|
|
260
|
+
message: "read record of Monitor",
|
|
261
|
+
}),
|
|
262
|
+
).not.toBe(base);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("an unattributed entity still yields a stable key", () => {
|
|
266
|
+
expect(
|
|
267
|
+
buildExceptionFailureModeKey({
|
|
268
|
+
primaryEntityId: undefined,
|
|
269
|
+
exceptionType: "Error",
|
|
270
|
+
message: "boom",
|
|
271
|
+
}),
|
|
272
|
+
).toBe(
|
|
273
|
+
buildExceptionFailureModeKey({
|
|
274
|
+
primaryEntityId: undefined,
|
|
275
|
+
exceptionType: "Error",
|
|
276
|
+
message: "boom",
|
|
277
|
+
}),
|
|
278
|
+
);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
154
282
|
describe("NewExceptionDetector.detect (IO wiring)", () => {
|
|
155
283
|
afterEach(() => {
|
|
156
284
|
jest.restoreAllMocks();
|
|
@@ -181,18 +309,35 @@ describe("NewExceptionDetector.detect (IO wiring)", () => {
|
|
|
181
309
|
firstSeenAt: expect.anything(),
|
|
182
310
|
occuranceCount: expect.anything(),
|
|
183
311
|
}),
|
|
184
|
-
limit:
|
|
312
|
+
limit: NEW_EXCEPTION_GROUP_SCAN_LIMIT,
|
|
185
313
|
props: expect.objectContaining({ isRoot: true }),
|
|
186
314
|
}),
|
|
187
315
|
);
|
|
188
316
|
|
|
317
|
+
/*
|
|
318
|
+
* The ORDER BY carries a tie-break. occuranceCount climbs with every
|
|
319
|
+
* ingested event, so the long tail sits densely tied right at the LIMIT
|
|
320
|
+
* and an untie-broken sort hands back a different arbitrary subset every
|
|
321
|
+
* tick.
|
|
322
|
+
*/
|
|
323
|
+
expect(findBySpy.mock.calls[0]![0]!.sort).toEqual({
|
|
324
|
+
occuranceCount: SortOrder.Descending,
|
|
325
|
+
firstSeenAt: SortOrder.Ascending,
|
|
326
|
+
});
|
|
327
|
+
|
|
189
328
|
expect(candidates).toHaveLength(1);
|
|
190
329
|
const candidate: InsightCandidate = candidates[0]!;
|
|
191
330
|
expect(candidate.insightType).toBe(AIInsightType.NewException);
|
|
192
331
|
expect(candidate.fingerprint).toBe(
|
|
193
|
-
`new-exception:${
|
|
332
|
+
`new-exception:${buildExceptionFailureModeKey({
|
|
333
|
+
primaryEntityId: serviceId,
|
|
334
|
+
exceptionType: "TypeError",
|
|
335
|
+
message: "Cannot read properties of undefined (reading 'user')",
|
|
336
|
+
})}`,
|
|
337
|
+
);
|
|
338
|
+
expect(candidate.title).toBe(
|
|
339
|
+
"New exception: TypeError: Cannot read properties of undefined (reading 'user') in web-api",
|
|
194
340
|
);
|
|
195
|
-
expect(candidate.title).toBe("New exception: TypeError in web-api");
|
|
196
341
|
expect(candidate.severity).toBe(AIInsightSeverity.High);
|
|
197
342
|
expect(candidate.serviceName).toBe("web-api");
|
|
198
343
|
expect(candidate.telemetryServiceId).toBe(serviceId);
|
|
@@ -201,11 +346,215 @@ describe("NewExceptionDetector.detect (IO wiring)", () => {
|
|
|
201
346
|
expect.objectContaining({
|
|
202
347
|
exceptionType: "TypeError",
|
|
203
348
|
totalOccurrenceCount: 57,
|
|
349
|
+
distinctExceptionGroupCount: 1,
|
|
204
350
|
firstSeenAt: "2026-07-14T02:00:00.000Z",
|
|
205
351
|
}),
|
|
206
352
|
);
|
|
207
353
|
expect(candidate.detailMarkdown).toContain("Occurrences so far: 57");
|
|
208
354
|
expect(candidate.detailMarkdown).toContain("web-api");
|
|
355
|
+
// A single group says nothing about stack variants.
|
|
356
|
+
expect(candidate.detailMarkdown).not.toContain("distinct stack traces");
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
/*
|
|
360
|
+
* THE REGRESSION THIS DETECTOR EXISTS TO PREVENT. The ingest fingerprint
|
|
361
|
+
* hashes the normalized stack trace, so one throw site reached from three
|
|
362
|
+
* call paths is three TelemetryException groups. Filing one insight per
|
|
363
|
+
* group is what produced a wall of identical "New exception: 401 in api"
|
|
364
|
+
* rows.
|
|
365
|
+
*/
|
|
366
|
+
test("stack-trace variants of one failure mode collapse into ONE candidate", async () => {
|
|
367
|
+
const variantIds: Array<ObjectID> = [
|
|
368
|
+
ObjectID.generate(),
|
|
369
|
+
ObjectID.generate(),
|
|
370
|
+
ObjectID.generate(),
|
|
371
|
+
];
|
|
372
|
+
|
|
373
|
+
jest.spyOn(TelemetryExceptionService, "findBy").mockResolvedValue([
|
|
374
|
+
fakeException({
|
|
375
|
+
id: variantIds[0]!,
|
|
376
|
+
exceptionType: "401",
|
|
377
|
+
message: "Unauthorized",
|
|
378
|
+
occuranceCount: 12,
|
|
379
|
+
firstSeenAt: new Date("2026-07-14T06:00:00.000Z"),
|
|
380
|
+
}),
|
|
381
|
+
fakeException({
|
|
382
|
+
id: variantIds[1]!,
|
|
383
|
+
exceptionType: "401",
|
|
384
|
+
message: "Unauthorized",
|
|
385
|
+
occuranceCount: 30,
|
|
386
|
+
firstSeenAt: new Date("2026-07-14T04:00:00.000Z"),
|
|
387
|
+
}),
|
|
388
|
+
fakeException({
|
|
389
|
+
id: variantIds[2]!,
|
|
390
|
+
exceptionType: "401",
|
|
391
|
+
message: "Unauthorized",
|
|
392
|
+
occuranceCount: 9,
|
|
393
|
+
firstSeenAt: new Date("2026-07-14T02:00:00.000Z"),
|
|
394
|
+
}),
|
|
395
|
+
] as unknown as Array<TelemetryException>);
|
|
396
|
+
jest
|
|
397
|
+
.spyOn(ServiceService, "findOneById")
|
|
398
|
+
.mockResolvedValue({ name: "api" } as unknown as Service);
|
|
399
|
+
|
|
400
|
+
const detector: NewExceptionDetector = new NewExceptionDetector();
|
|
401
|
+
const candidates: Array<InsightCandidate> = await detector.detect({
|
|
402
|
+
projectId,
|
|
403
|
+
now,
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
expect(candidates).toHaveLength(1);
|
|
407
|
+
const candidate: InsightCandidate = candidates[0]!;
|
|
408
|
+
|
|
409
|
+
// Counts are summed, so severity reflects the whole failure mode.
|
|
410
|
+
expect(candidate.evidence.exception?.totalOccurrenceCount).toBe(51);
|
|
411
|
+
expect(candidate.evidence.exception?.distinctExceptionGroupCount).toBe(3);
|
|
412
|
+
expect(candidate.severity).toBe(AIInsightSeverity.High);
|
|
413
|
+
// The earliest member dates the finding.
|
|
414
|
+
expect(candidate.evidence.exception?.firstSeenAt).toBe(
|
|
415
|
+
"2026-07-14T02:00:00.000Z",
|
|
416
|
+
);
|
|
417
|
+
// The loudest member is what the insight links to.
|
|
418
|
+
expect(candidate.telemetryExceptionId).toBe(variantIds[1]!);
|
|
419
|
+
expect(candidate.detailMarkdown).toContain(
|
|
420
|
+
"Raised from 3 distinct stack traces",
|
|
421
|
+
);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test("the fingerprint does not move when the loudest member changes", async () => {
|
|
425
|
+
const quiet: ObjectID = ObjectID.generate();
|
|
426
|
+
const loud: ObjectID = ObjectID.generate();
|
|
427
|
+
|
|
428
|
+
jest
|
|
429
|
+
.spyOn(ServiceService, "findOneById")
|
|
430
|
+
.mockResolvedValue({ name: "api" } as unknown as Service);
|
|
431
|
+
|
|
432
|
+
type FingerprintForOrderFunction = (
|
|
433
|
+
order: Array<[ObjectID, number]>,
|
|
434
|
+
) => Promise<string>;
|
|
435
|
+
|
|
436
|
+
const fingerprintForOrder: FingerprintForOrderFunction = async (
|
|
437
|
+
order: Array<[ObjectID, number]>,
|
|
438
|
+
): Promise<string> => {
|
|
439
|
+
jest.spyOn(TelemetryExceptionService, "findBy").mockResolvedValue(
|
|
440
|
+
order.map(([id, count]: [ObjectID, number]) => {
|
|
441
|
+
return fakeException({
|
|
442
|
+
id: id,
|
|
443
|
+
exceptionType: "401",
|
|
444
|
+
message: "Unauthorized",
|
|
445
|
+
occuranceCount: count,
|
|
446
|
+
});
|
|
447
|
+
}) as unknown as Array<TelemetryException>,
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
const detector: NewExceptionDetector = new NewExceptionDetector();
|
|
451
|
+
const candidates: Array<InsightCandidate> = await detector.detect({
|
|
452
|
+
projectId,
|
|
453
|
+
now,
|
|
454
|
+
});
|
|
455
|
+
return candidates[0]!.fingerprint;
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
const before: string = await fingerprintForOrder([
|
|
459
|
+
[quiet, 40],
|
|
460
|
+
[loud, 5],
|
|
461
|
+
]);
|
|
462
|
+
const after: string = await fingerprintForOrder([
|
|
463
|
+
[loud, 900],
|
|
464
|
+
[quiet, 40],
|
|
465
|
+
]);
|
|
466
|
+
|
|
467
|
+
expect(before).toBe(after);
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
test("distinct failure modes stay distinct, and the loudest is first", async () => {
|
|
471
|
+
jest.spyOn(TelemetryExceptionService, "findBy").mockResolvedValue([
|
|
472
|
+
fakeException({
|
|
473
|
+
id: ObjectID.generate(),
|
|
474
|
+
exceptionType: "401",
|
|
475
|
+
message: "read record of Monitor",
|
|
476
|
+
occuranceCount: 8,
|
|
477
|
+
}),
|
|
478
|
+
fakeException({
|
|
479
|
+
id: ObjectID.generate(),
|
|
480
|
+
exceptionType: "401",
|
|
481
|
+
message: "read record of Incident",
|
|
482
|
+
occuranceCount: 60,
|
|
483
|
+
}),
|
|
484
|
+
] as unknown as Array<TelemetryException>);
|
|
485
|
+
jest
|
|
486
|
+
.spyOn(ServiceService, "findOneById")
|
|
487
|
+
.mockResolvedValue({ name: "api" } as unknown as Service);
|
|
488
|
+
|
|
489
|
+
const detector: NewExceptionDetector = new NewExceptionDetector();
|
|
490
|
+
const candidates: Array<InsightCandidate> = await detector.detect({
|
|
491
|
+
projectId,
|
|
492
|
+
now,
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
expect(candidates).toHaveLength(2);
|
|
496
|
+
expect(candidates[0]!.fingerprint).not.toBe(candidates[1]!.fingerprint);
|
|
497
|
+
expect(candidates[0]!.title).not.toBe(candidates[1]!.title);
|
|
498
|
+
expect(candidates[0]!.evidence.exception?.totalOccurrenceCount).toBe(60);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
test("caps emitted candidates at the candidate limit, not the scan limit", async () => {
|
|
502
|
+
const groups: Array<TelemetryException> = [];
|
|
503
|
+
for (let i: number = 0; i < NEW_EXCEPTION_GROUP_SCAN_LIMIT; i++) {
|
|
504
|
+
groups.push(
|
|
505
|
+
fakeException({
|
|
506
|
+
id: ObjectID.generate(),
|
|
507
|
+
exceptionType: "Error",
|
|
508
|
+
message: `distinct failure ${i}`,
|
|
509
|
+
occuranceCount: 5,
|
|
510
|
+
}),
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
jest
|
|
515
|
+
.spyOn(TelemetryExceptionService, "findBy")
|
|
516
|
+
.mockResolvedValue(groups as unknown as Array<TelemetryException>);
|
|
517
|
+
jest.spyOn(ServiceService, "findOneById").mockResolvedValue(null);
|
|
518
|
+
|
|
519
|
+
const detector: NewExceptionDetector = new NewExceptionDetector();
|
|
520
|
+
const candidates: Array<InsightCandidate> = await detector.detect({
|
|
521
|
+
projectId,
|
|
522
|
+
now,
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
expect(candidates).toHaveLength(NEW_EXCEPTION_CANDIDATE_LIMIT);
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
test("resolves each service name once, however many groups share it", async () => {
|
|
529
|
+
jest.spyOn(TelemetryExceptionService, "findBy").mockResolvedValue([
|
|
530
|
+
fakeException({
|
|
531
|
+
id: ObjectID.generate(),
|
|
532
|
+
message: "first failure",
|
|
533
|
+
occuranceCount: 10,
|
|
534
|
+
}),
|
|
535
|
+
fakeException({
|
|
536
|
+
id: ObjectID.generate(),
|
|
537
|
+
message: "second failure",
|
|
538
|
+
occuranceCount: 9,
|
|
539
|
+
}),
|
|
540
|
+
fakeException({
|
|
541
|
+
id: ObjectID.generate(),
|
|
542
|
+
message: "third failure",
|
|
543
|
+
occuranceCount: 8,
|
|
544
|
+
}),
|
|
545
|
+
] as unknown as Array<TelemetryException>);
|
|
546
|
+
const findOneByIdSpy: jest.SpyInstance = jest
|
|
547
|
+
.spyOn(ServiceService, "findOneById")
|
|
548
|
+
.mockResolvedValue({ name: "web-api" } as unknown as Service);
|
|
549
|
+
|
|
550
|
+
const detector: NewExceptionDetector = new NewExceptionDetector();
|
|
551
|
+
const candidates: Array<InsightCandidate> = await detector.detect({
|
|
552
|
+
projectId,
|
|
553
|
+
now,
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
expect(candidates).toHaveLength(3);
|
|
557
|
+
expect(findOneByIdSpy).toHaveBeenCalledTimes(1);
|
|
209
558
|
});
|
|
210
559
|
|
|
211
560
|
test("unresolvable service (host/cluster entity) ships without a service name", async () => {
|
|
@@ -223,7 +572,9 @@ describe("NewExceptionDetector.detect (IO wiring)", () => {
|
|
|
223
572
|
});
|
|
224
573
|
|
|
225
574
|
expect(candidates).toHaveLength(1);
|
|
226
|
-
expect(candidates[0]!.title).toBe(
|
|
575
|
+
expect(candidates[0]!.title).toBe(
|
|
576
|
+
"New exception: TypeError: Cannot read properties of undefined (reading 'user')",
|
|
577
|
+
);
|
|
227
578
|
expect(candidates[0]!.serviceName).toBeUndefined();
|
|
228
579
|
expect(candidates[0]!.telemetryServiceId).toBeUndefined();
|
|
229
580
|
expect(candidates[0]!.severity).toBe(AIInsightSeverity.Medium);
|