@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
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
import http from "http";
|
|
2
|
+
import https from "https";
|
|
3
|
+
import axios, { AxiosRequestConfig } from "axios";
|
|
4
|
+
import {
|
|
5
|
+
EgressGuardOptions,
|
|
6
|
+
ResolvedAddress,
|
|
7
|
+
} from "../../../../Server/Utils/DataSource/EgressGuard";
|
|
8
|
+
import DataSourceHttpFetch, {
|
|
9
|
+
DataSourceHttpRequest,
|
|
10
|
+
DataSourceHttpResponse,
|
|
11
|
+
} from "../../../../Server/Utils/DataSource/HttpFetch";
|
|
12
|
+
import { DataSourceConnectionSettings } from "../../../../Server/Utils/DataSource/Types";
|
|
13
|
+
import {
|
|
14
|
+
DATA_SOURCE_MAX_RESPONSE_SIZE_IN_BYTES,
|
|
15
|
+
DATA_SOURCE_QUERY_TIMEOUT_IN_MS,
|
|
16
|
+
} from "../../../../Types/DataSource/DataSourceLimits";
|
|
17
|
+
import DataSourceType from "../../../../Types/DataSource/DataSourceType";
|
|
18
|
+
import Dictionary from "../../../../Types/Dictionary";
|
|
19
|
+
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
20
|
+
import { beforeEach, describe, expect, test } from "@jest/globals";
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
* Hermetic tests for the SSRF-hardened HTTP client. axios is mocked at the
|
|
24
|
+
* module boundary so no socket is ever opened; every assertion is made
|
|
25
|
+
* against the config object axios ACTUALLY received, because that config IS
|
|
26
|
+
* the security contract (no redirects, response caps, pinned lookup).
|
|
27
|
+
*/
|
|
28
|
+
jest.mock("axios", () => {
|
|
29
|
+
type IsAxiosErrorFunction = (candidate: unknown) => boolean;
|
|
30
|
+
const isAxiosError: IsAxiosErrorFunction = (candidate: unknown): boolean => {
|
|
31
|
+
return Boolean(
|
|
32
|
+
candidate &&
|
|
33
|
+
typeof candidate === "object" &&
|
|
34
|
+
(candidate as { isAxiosError?: boolean }).isAxiosError === true,
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
const axiosFunction: jest.Mock = Object.assign(jest.fn(), {
|
|
38
|
+
isAxiosError: isAxiosError,
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
__esModule: true,
|
|
42
|
+
default: axiosFunction,
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const axiosMock: jest.Mock = axios as unknown as jest.Mock;
|
|
47
|
+
|
|
48
|
+
const PUBLIC_IP: string = "93.184.216.34";
|
|
49
|
+
const PUBLIC_IPV6: string = "2606:2800:220:1:248:1893:25c8:1946";
|
|
50
|
+
|
|
51
|
+
interface RecordingEgress {
|
|
52
|
+
options: EgressGuardOptions;
|
|
53
|
+
calls: Array<string>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function makeEgress(addresses?: Array<ResolvedAddress>): RecordingEgress {
|
|
57
|
+
const calls: Array<string> = [];
|
|
58
|
+
const resolved: Array<ResolvedAddress> = addresses || [
|
|
59
|
+
{ address: PUBLIC_IP, family: 4 },
|
|
60
|
+
];
|
|
61
|
+
const options: EgressGuardOptions = {
|
|
62
|
+
blockPrivateAddresses: true,
|
|
63
|
+
resolveFunction: (hostname: string): Promise<Array<ResolvedAddress>> => {
|
|
64
|
+
calls.push(hostname);
|
|
65
|
+
return Promise.resolve(resolved);
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
return { options: options, calls: calls };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function makeRequest(
|
|
72
|
+
overrides?: Partial<DataSourceHttpRequest>,
|
|
73
|
+
): DataSourceHttpRequest {
|
|
74
|
+
return {
|
|
75
|
+
method: "GET",
|
|
76
|
+
url: "https://api.example.com/v1/query?limit=10",
|
|
77
|
+
egressOptions: makeEgress().options,
|
|
78
|
+
...overrides,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getAxiosConfig(callIndex: number = 0): AxiosRequestConfig {
|
|
83
|
+
expect(axiosMock.mock.calls.length).toBeGreaterThan(callIndex);
|
|
84
|
+
return axiosMock.mock.calls[callIndex]![0] as AxiosRequestConfig;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
type CaptureRejectionFunction = (promise: Promise<unknown>) => Promise<Error>;
|
|
88
|
+
|
|
89
|
+
const captureRejection: CaptureRejectionFunction = async (
|
|
90
|
+
promise: Promise<unknown>,
|
|
91
|
+
): Promise<Error> => {
|
|
92
|
+
try {
|
|
93
|
+
await promise;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return error as Error;
|
|
96
|
+
}
|
|
97
|
+
throw new Error("Expected promise to reject, but it resolved.");
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
interface LookupResultEntry {
|
|
101
|
+
address: string;
|
|
102
|
+
family: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
type PinnedLookupCallback = (
|
|
106
|
+
error: NodeJS.ErrnoException | null,
|
|
107
|
+
address: string | Array<LookupResultEntry>,
|
|
108
|
+
family?: number,
|
|
109
|
+
) => void;
|
|
110
|
+
|
|
111
|
+
type PinnedLookup = (
|
|
112
|
+
hostname: string,
|
|
113
|
+
options: { all?: boolean },
|
|
114
|
+
callback: PinnedLookupCallback,
|
|
115
|
+
) => void;
|
|
116
|
+
|
|
117
|
+
interface AgentWithLookupOption {
|
|
118
|
+
options: { lookup?: PinnedLookup };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function getPinnedLookup(agent: unknown): PinnedLookup {
|
|
122
|
+
const lookup: PinnedLookup | undefined = (agent as AgentWithLookupOption)
|
|
123
|
+
.options.lookup;
|
|
124
|
+
expect(typeof lookup).toBe("function");
|
|
125
|
+
return lookup!;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
interface LookupOutcome {
|
|
129
|
+
address: string | Array<LookupResultEntry>;
|
|
130
|
+
family: number | undefined;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function runLookup(
|
|
134
|
+
lookup: PinnedLookup,
|
|
135
|
+
hostname: string,
|
|
136
|
+
options: { all?: boolean },
|
|
137
|
+
): Promise<LookupOutcome> {
|
|
138
|
+
return new Promise(
|
|
139
|
+
(
|
|
140
|
+
resolve: (outcome: LookupOutcome) => void,
|
|
141
|
+
reject: (error: Error) => void,
|
|
142
|
+
) => {
|
|
143
|
+
lookup(
|
|
144
|
+
hostname,
|
|
145
|
+
options,
|
|
146
|
+
(
|
|
147
|
+
error: NodeJS.ErrnoException | null,
|
|
148
|
+
address: string | Array<LookupResultEntry>,
|
|
149
|
+
family?: number,
|
|
150
|
+
): void => {
|
|
151
|
+
if (error) {
|
|
152
|
+
reject(error);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
resolve({ address: address, family: family });
|
|
156
|
+
},
|
|
157
|
+
);
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
beforeEach(() => {
|
|
163
|
+
axiosMock.mockReset();
|
|
164
|
+
axiosMock.mockResolvedValue({ status: 200, data: "{}" });
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe("DataSourceHttpFetch.fetch - hardened axios config", () => {
|
|
168
|
+
test("refuses redirects: maxRedirects is 0", async () => {
|
|
169
|
+
await DataSourceHttpFetch.fetch(makeRequest());
|
|
170
|
+
expect(getAxiosConfig().maxRedirects).toBe(0);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("caps response and request body size at DATA_SOURCE_MAX_RESPONSE_SIZE_IN_BYTES", async () => {
|
|
174
|
+
await DataSourceHttpFetch.fetch(makeRequest());
|
|
175
|
+
const config: AxiosRequestConfig = getAxiosConfig();
|
|
176
|
+
expect(config.maxContentLength).toBe(
|
|
177
|
+
DATA_SOURCE_MAX_RESPONSE_SIZE_IN_BYTES,
|
|
178
|
+
);
|
|
179
|
+
expect(config.maxBodyLength).toBe(DATA_SOURCE_MAX_RESPONSE_SIZE_IN_BYTES);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("defaults the timeout to DATA_SOURCE_QUERY_TIMEOUT_IN_MS", async () => {
|
|
183
|
+
await DataSourceHttpFetch.fetch(makeRequest());
|
|
184
|
+
expect(getAxiosConfig().timeout).toBe(DATA_SOURCE_QUERY_TIMEOUT_IN_MS);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("honors an explicit timeoutInMs", async () => {
|
|
188
|
+
await DataSourceHttpFetch.fetch(makeRequest({ timeoutInMs: 1234 }));
|
|
189
|
+
expect(getAxiosConfig().timeout).toBe(1234);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("passes the guard-validated URL and method through to axios", async () => {
|
|
193
|
+
await DataSourceHttpFetch.fetch(makeRequest());
|
|
194
|
+
const config: AxiosRequestConfig = getAxiosConfig();
|
|
195
|
+
expect(config.url).toBe("https://api.example.com/v1/query?limit=10");
|
|
196
|
+
expect(config.method).toBe("GET");
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("keeps the raw body: responseType text with an identity transform", async () => {
|
|
200
|
+
await DataSourceHttpFetch.fetch(makeRequest());
|
|
201
|
+
const config: AxiosRequestConfig = getAxiosConfig();
|
|
202
|
+
expect(config.responseType).toBe("text");
|
|
203
|
+
const transforms: Array<(body: string) => string> =
|
|
204
|
+
config.transformResponse as Array<(body: string) => string>;
|
|
205
|
+
expect(Array.isArray(transforms)).toBe(true);
|
|
206
|
+
expect(transforms).toHaveLength(1);
|
|
207
|
+
expect(transforms[0]!('{"not":"parsed"}')).toBe('{"not":"parsed"}');
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("validateStatus accepts only statuses below 300 and refuses every 3xx/4xx/5xx", async () => {
|
|
211
|
+
await DataSourceHttpFetch.fetch(makeRequest());
|
|
212
|
+
const validateStatus: (status: number) => boolean = getAxiosConfig()
|
|
213
|
+
.validateStatus as (status: number) => boolean;
|
|
214
|
+
|
|
215
|
+
const redirectStatuses: Array<number> = [301, 302, 303, 307, 308];
|
|
216
|
+
for (const status of redirectStatuses) {
|
|
217
|
+
expect(validateStatus(status)).toBe(false);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Contract is `status < 300`: every 2xx (and 1xx) passes...
|
|
221
|
+
expect(validateStatus(200)).toBe(true);
|
|
222
|
+
expect(validateStatus(204)).toBe(true);
|
|
223
|
+
expect(validateStatus(299)).toBe(true);
|
|
224
|
+
expect(validateStatus(101)).toBe(true);
|
|
225
|
+
|
|
226
|
+
// ...and every 4xx/5xx throws so connectors surface the body as an error.
|
|
227
|
+
const errorStatuses: Array<number> = [300, 400, 401, 404, 429, 500, 503];
|
|
228
|
+
for (const status of errorStatuses) {
|
|
229
|
+
expect(validateStatus(status)).toBe(false);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe("DataSourceHttpFetch.fetch - pinned socket lookup", () => {
|
|
235
|
+
test("provides per-request http and https agents", async () => {
|
|
236
|
+
await DataSourceHttpFetch.fetch(makeRequest());
|
|
237
|
+
await DataSourceHttpFetch.fetch(makeRequest());
|
|
238
|
+
|
|
239
|
+
const first: AxiosRequestConfig = getAxiosConfig(0);
|
|
240
|
+
const second: AxiosRequestConfig = getAxiosConfig(1);
|
|
241
|
+
|
|
242
|
+
expect(first.httpAgent).toBeInstanceOf(http.Agent);
|
|
243
|
+
expect(first.httpsAgent).toBeInstanceOf(https.Agent);
|
|
244
|
+
expect(second.httpAgent).toBeInstanceOf(http.Agent);
|
|
245
|
+
expect(second.httpsAgent).toBeInstanceOf(https.Agent);
|
|
246
|
+
|
|
247
|
+
// Fresh agents per request — no lookup state shared across requests.
|
|
248
|
+
expect(first.httpAgent).not.toBe(second.httpAgent);
|
|
249
|
+
expect(first.httpsAgent).not.toBe(second.httpsAgent);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("the agent lookup yields the pinned validated address, ignoring the hostname it is asked for", async () => {
|
|
253
|
+
await DataSourceHttpFetch.fetch(makeRequest());
|
|
254
|
+
const lookup: PinnedLookup = getPinnedLookup(getAxiosConfig().httpsAgent);
|
|
255
|
+
|
|
256
|
+
/*
|
|
257
|
+
* Even if DNS would now answer differently (rebind), the lookup must
|
|
258
|
+
* return the address that was validated — for ANY hostname asked.
|
|
259
|
+
*/
|
|
260
|
+
const outcome: LookupOutcome = await runLookup(
|
|
261
|
+
lookup,
|
|
262
|
+
"rebound.attacker.example",
|
|
263
|
+
{},
|
|
264
|
+
);
|
|
265
|
+
expect(outcome.address).toBe(PUBLIC_IP);
|
|
266
|
+
expect(outcome.family).toBe(4);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("the agent lookup returns every pinned address when asked for all", async () => {
|
|
270
|
+
const egress: RecordingEgress = makeEgress([
|
|
271
|
+
{ address: PUBLIC_IP, family: 4 },
|
|
272
|
+
{ address: PUBLIC_IPV6, family: 6 },
|
|
273
|
+
]);
|
|
274
|
+
await DataSourceHttpFetch.fetch(
|
|
275
|
+
makeRequest({ egressOptions: egress.options }),
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
const lookup: PinnedLookup = getPinnedLookup(getAxiosConfig().httpsAgent);
|
|
279
|
+
const allOutcome: LookupOutcome = await runLookup(
|
|
280
|
+
lookup,
|
|
281
|
+
"api.example.com",
|
|
282
|
+
{ all: true },
|
|
283
|
+
);
|
|
284
|
+
expect(allOutcome.address).toEqual([
|
|
285
|
+
{ address: PUBLIC_IP, family: 4 },
|
|
286
|
+
{ address: PUBLIC_IPV6, family: 6 },
|
|
287
|
+
]);
|
|
288
|
+
|
|
289
|
+
// Single-address mode pins the first validated address.
|
|
290
|
+
const singleOutcome: LookupOutcome = await runLookup(
|
|
291
|
+
lookup,
|
|
292
|
+
"api.example.com",
|
|
293
|
+
{},
|
|
294
|
+
);
|
|
295
|
+
expect(singleOutcome.address).toBe(PUBLIC_IP);
|
|
296
|
+
expect(singleOutcome.family).toBe(4);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("both the http and https agents carry the pinned lookup", async () => {
|
|
300
|
+
await DataSourceHttpFetch.fetch(makeRequest());
|
|
301
|
+
const config: AxiosRequestConfig = getAxiosConfig();
|
|
302
|
+
const httpOutcome: LookupOutcome = await runLookup(
|
|
303
|
+
getPinnedLookup(config.httpAgent),
|
|
304
|
+
"api.example.com",
|
|
305
|
+
{},
|
|
306
|
+
);
|
|
307
|
+
const httpsOutcome: LookupOutcome = await runLookup(
|
|
308
|
+
getPinnedLookup(config.httpsAgent),
|
|
309
|
+
"api.example.com",
|
|
310
|
+
{},
|
|
311
|
+
);
|
|
312
|
+
expect(httpOutcome.address).toBe(PUBLIC_IP);
|
|
313
|
+
expect(httpsOutcome.address).toBe(PUBLIC_IP);
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
describe("DataSourceHttpFetch.fetch - egress guard runs before axios", () => {
|
|
318
|
+
test("forwards egressOptions and resolves the URL hostname exactly once", async () => {
|
|
319
|
+
const egress: RecordingEgress = makeEgress();
|
|
320
|
+
await DataSourceHttpFetch.fetch(
|
|
321
|
+
makeRequest({ egressOptions: egress.options }),
|
|
322
|
+
);
|
|
323
|
+
expect(egress.calls).toEqual(["api.example.com"]);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test("a blocked literal host rejects and axios is never called", async () => {
|
|
327
|
+
const error: Error = await captureRejection(
|
|
328
|
+
DataSourceHttpFetch.fetch(
|
|
329
|
+
makeRequest({
|
|
330
|
+
url: "http://169.254.169.254/latest/meta-data",
|
|
331
|
+
egressOptions: { blockPrivateAddresses: false },
|
|
332
|
+
}),
|
|
333
|
+
),
|
|
334
|
+
);
|
|
335
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
336
|
+
expect(error.message).toContain("169.254.169.254");
|
|
337
|
+
expect(error.message).toContain("link-local");
|
|
338
|
+
expect(axiosMock).not.toHaveBeenCalled();
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test("a hostname resolving to loopback rejects and axios is never called", async () => {
|
|
342
|
+
const egress: RecordingEgress = makeEgress([
|
|
343
|
+
{ address: "127.0.0.1", family: 4 },
|
|
344
|
+
]);
|
|
345
|
+
const error: Error = await captureRejection(
|
|
346
|
+
DataSourceHttpFetch.fetch(
|
|
347
|
+
makeRequest({
|
|
348
|
+
url: "https://evil.example.com/status",
|
|
349
|
+
egressOptions: egress.options,
|
|
350
|
+
}),
|
|
351
|
+
),
|
|
352
|
+
);
|
|
353
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
354
|
+
expect(error.message).toContain("evil.example.com");
|
|
355
|
+
expect(error.message).toContain("loopback");
|
|
356
|
+
expect(axiosMock).not.toHaveBeenCalled();
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
test("a non-http(s) scheme rejects and axios is never called", async () => {
|
|
360
|
+
const error: Error = await captureRejection(
|
|
361
|
+
DataSourceHttpFetch.fetch(makeRequest({ url: "file:///etc/passwd" })),
|
|
362
|
+
);
|
|
363
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
364
|
+
expect(error.message).toContain("http or https");
|
|
365
|
+
expect(axiosMock).not.toHaveBeenCalled();
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
describe("DataSourceHttpFetch.fetch - headers and body encoding", () => {
|
|
370
|
+
test("passes request headers through to axios untouched", async () => {
|
|
371
|
+
const headers: Dictionary<string> = {
|
|
372
|
+
"X-Api-Key": "secret-key",
|
|
373
|
+
Authorization: "Bearer token-123",
|
|
374
|
+
};
|
|
375
|
+
await DataSourceHttpFetch.fetch(makeRequest({ headers: headers }));
|
|
376
|
+
const config: AxiosRequestConfig = getAxiosConfig();
|
|
377
|
+
expect(config.headers).toEqual(headers);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
test("GET requests never send a body, even when one is provided", async () => {
|
|
381
|
+
await DataSourceHttpFetch.fetch(
|
|
382
|
+
makeRequest({ method: "GET", body: "should-not-be-sent" }),
|
|
383
|
+
);
|
|
384
|
+
expect(getAxiosConfig().data).toBeUndefined();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
test("POST string body is sent as-is with a default JSON content type", async () => {
|
|
388
|
+
await DataSourceHttpFetch.fetch(
|
|
389
|
+
makeRequest({ method: "POST", body: '{"raw":"body"}' }),
|
|
390
|
+
);
|
|
391
|
+
const config: AxiosRequestConfig = getAxiosConfig();
|
|
392
|
+
expect(config.data).toBe('{"raw":"body"}');
|
|
393
|
+
expect((config.headers as Dictionary<string>)["Content-Type"]).toBe(
|
|
394
|
+
"application/json",
|
|
395
|
+
);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
test("POST string body respects an explicit Content-Type header", async () => {
|
|
399
|
+
await DataSourceHttpFetch.fetch(
|
|
400
|
+
makeRequest({
|
|
401
|
+
method: "POST",
|
|
402
|
+
body: "plain text",
|
|
403
|
+
headers: { "Content-Type": "text/plain" },
|
|
404
|
+
}),
|
|
405
|
+
);
|
|
406
|
+
const config: AxiosRequestConfig = getAxiosConfig();
|
|
407
|
+
expect(config.data).toBe("plain text");
|
|
408
|
+
expect((config.headers as Dictionary<string>)["Content-Type"]).toBe(
|
|
409
|
+
"text/plain",
|
|
410
|
+
);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test("POST object body with formUrlEncoded is sent as URLSearchParams", async () => {
|
|
414
|
+
await DataSourceHttpFetch.fetch(
|
|
415
|
+
makeRequest({
|
|
416
|
+
method: "POST",
|
|
417
|
+
body: { query: "up", start: "123" },
|
|
418
|
+
formUrlEncoded: true,
|
|
419
|
+
}),
|
|
420
|
+
);
|
|
421
|
+
const config: AxiosRequestConfig = getAxiosConfig();
|
|
422
|
+
expect(config.data).toBeInstanceOf(URLSearchParams);
|
|
423
|
+
expect((config.data as URLSearchParams).get("query")).toBe("up");
|
|
424
|
+
expect((config.data as URLSearchParams).get("start")).toBe("123");
|
|
425
|
+
expect((config.headers as Dictionary<string>)["Content-Type"]).toBe(
|
|
426
|
+
"application/x-www-form-urlencoded",
|
|
427
|
+
);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
test("POST object body without formUrlEncoded is JSON-encoded", async () => {
|
|
431
|
+
await DataSourceHttpFetch.fetch(
|
|
432
|
+
makeRequest({ method: "POST", body: { key: "value" } }),
|
|
433
|
+
);
|
|
434
|
+
const config: AxiosRequestConfig = getAxiosConfig();
|
|
435
|
+
expect(config.data).toBe('{"key":"value"}');
|
|
436
|
+
expect((config.headers as Dictionary<string>)["Content-Type"]).toBe(
|
|
437
|
+
"application/json",
|
|
438
|
+
);
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
describe("DataSourceHttpFetch.buildAuthHeaders", () => {
|
|
443
|
+
function makeSettings(
|
|
444
|
+
overrides?: Partial<DataSourceConnectionSettings>,
|
|
445
|
+
): DataSourceConnectionSettings {
|
|
446
|
+
return {
|
|
447
|
+
dataSourceType: DataSourceType.RestApi,
|
|
448
|
+
url: "https://api.example.com",
|
|
449
|
+
...overrides,
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
test("returns no headers for settings without credentials", () => {
|
|
454
|
+
expect(DataSourceHttpFetch.buildAuthHeaders(makeSettings())).toEqual({});
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
test("apiToken becomes a Bearer Authorization header", () => {
|
|
458
|
+
const headers: Dictionary<string> = DataSourceHttpFetch.buildAuthHeaders(
|
|
459
|
+
makeSettings({ apiToken: "tok-abc" }),
|
|
460
|
+
);
|
|
461
|
+
expect(headers).toEqual({ Authorization: "Bearer tok-abc" });
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
test("username and password become HTTP basic auth", () => {
|
|
465
|
+
const headers: Dictionary<string> = DataSourceHttpFetch.buildAuthHeaders(
|
|
466
|
+
makeSettings({ username: "user", password: "pass" }),
|
|
467
|
+
);
|
|
468
|
+
const expected: string = Buffer.from("user:pass").toString("base64");
|
|
469
|
+
expect(headers).toEqual({ Authorization: `Basic ${expected}` });
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
test("username without password still produces basic auth", () => {
|
|
473
|
+
const headers: Dictionary<string> = DataSourceHttpFetch.buildAuthHeaders(
|
|
474
|
+
makeSettings({ username: "user" }),
|
|
475
|
+
);
|
|
476
|
+
const expected: string = Buffer.from("user:").toString("base64");
|
|
477
|
+
expect(headers).toEqual({ Authorization: `Basic ${expected}` });
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
test("apiToken wins over username/password", () => {
|
|
481
|
+
const headers: Dictionary<string> = DataSourceHttpFetch.buildAuthHeaders(
|
|
482
|
+
makeSettings({ apiToken: "tok", username: "user", password: "pass" }),
|
|
483
|
+
);
|
|
484
|
+
expect(headers).toEqual({ Authorization: "Bearer tok" });
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
test("customHeaders are applied last and may override Authorization", () => {
|
|
488
|
+
const headers: Dictionary<string> = DataSourceHttpFetch.buildAuthHeaders(
|
|
489
|
+
makeSettings({
|
|
490
|
+
apiToken: "tok",
|
|
491
|
+
customHeaders: {
|
|
492
|
+
Authorization: "Custom scheme",
|
|
493
|
+
"X-Extra": "extra-value",
|
|
494
|
+
},
|
|
495
|
+
}),
|
|
496
|
+
);
|
|
497
|
+
expect(headers).toEqual({
|
|
498
|
+
Authorization: "Custom scheme",
|
|
499
|
+
"X-Extra": "extra-value",
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
describe("DataSourceHttpFetch.fetch - response handling", () => {
|
|
505
|
+
test("returns status, raw body text and parsed JSON on success", async () => {
|
|
506
|
+
axiosMock.mockResolvedValue({ status: 200, data: '{"ok":true,"n":2}' });
|
|
507
|
+
const response: DataSourceHttpResponse =
|
|
508
|
+
await DataSourceHttpFetch.fetch(makeRequest());
|
|
509
|
+
expect(response.statusCode).toBe(200);
|
|
510
|
+
expect(response.bodyText).toBe('{"ok":true,"n":2}');
|
|
511
|
+
expect(response.bodyJson).toEqual({ ok: true, n: 2 });
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
test("leaves bodyJson undefined for a non-JSON body", async () => {
|
|
515
|
+
axiosMock.mockResolvedValue({ status: 200, data: "plain text response" });
|
|
516
|
+
const response: DataSourceHttpResponse =
|
|
517
|
+
await DataSourceHttpFetch.fetch(makeRequest());
|
|
518
|
+
expect(response.bodyText).toBe("plain text response");
|
|
519
|
+
expect(response.bodyJson).toBeUndefined();
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
test("normalizes an empty body to an empty string", async () => {
|
|
523
|
+
axiosMock.mockResolvedValue({ status: 204, data: "" });
|
|
524
|
+
const response: DataSourceHttpResponse =
|
|
525
|
+
await DataSourceHttpFetch.fetch(makeRequest());
|
|
526
|
+
expect(response.statusCode).toBe(204);
|
|
527
|
+
expect(response.bodyText).toBe("");
|
|
528
|
+
expect(response.bodyJson).toBeUndefined();
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
describe("DataSourceHttpFetch.fetch - error mapping", () => {
|
|
533
|
+
test("a 302 answer rejects as BadDataException instead of following the redirect", async () => {
|
|
534
|
+
/*
|
|
535
|
+
* With validateStatus refusing 3xx and maxRedirects 0, axios rejects
|
|
536
|
+
* with an error carrying the response — simulate exactly that shape.
|
|
537
|
+
*/
|
|
538
|
+
axiosMock.mockRejectedValue({
|
|
539
|
+
isAxiosError: true,
|
|
540
|
+
message: "Request failed with status code 302",
|
|
541
|
+
response: { status: 302, data: "" },
|
|
542
|
+
});
|
|
543
|
+
const error: Error = await captureRejection(
|
|
544
|
+
DataSourceHttpFetch.fetch(makeRequest()),
|
|
545
|
+
);
|
|
546
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
547
|
+
expect(error.message).toContain("Data source responded with HTTP 302");
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
test("an HTTP error status surfaces the status and the body excerpt", async () => {
|
|
551
|
+
axiosMock.mockRejectedValue({
|
|
552
|
+
isAxiosError: true,
|
|
553
|
+
message: "Request failed with status code 500",
|
|
554
|
+
response: { status: 500, data: "parse error at char 5" },
|
|
555
|
+
});
|
|
556
|
+
const error: Error = await captureRejection(
|
|
557
|
+
DataSourceHttpFetch.fetch(makeRequest()),
|
|
558
|
+
);
|
|
559
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
560
|
+
expect(error.message).toContain("Data source responded with HTTP 500");
|
|
561
|
+
expect(error.message).toContain("parse error at char 5");
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
test("a non-string error body is JSON-encoded into the message", async () => {
|
|
565
|
+
axiosMock.mockRejectedValue({
|
|
566
|
+
isAxiosError: true,
|
|
567
|
+
message: "Request failed with status code 400",
|
|
568
|
+
response: { status: 400, data: { error: "bad query" } },
|
|
569
|
+
});
|
|
570
|
+
const error: Error = await captureRejection(
|
|
571
|
+
DataSourceHttpFetch.fetch(makeRequest()),
|
|
572
|
+
);
|
|
573
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
574
|
+
expect(error.message).toContain("HTTP 400");
|
|
575
|
+
expect(error.message).toContain('{"error":"bad query"}');
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
test("the error body excerpt is capped at 500 characters", async () => {
|
|
579
|
+
axiosMock.mockRejectedValue({
|
|
580
|
+
isAxiosError: true,
|
|
581
|
+
message: "Request failed with status code 500",
|
|
582
|
+
response: { status: 500, data: "x".repeat(600) },
|
|
583
|
+
});
|
|
584
|
+
const error: Error = await captureRejection(
|
|
585
|
+
DataSourceHttpFetch.fetch(makeRequest()),
|
|
586
|
+
);
|
|
587
|
+
expect(error.message).toContain("x".repeat(500));
|
|
588
|
+
expect(error.message).not.toContain("x".repeat(501));
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
test("a connection failure maps to a friendly could-not-reach error", async () => {
|
|
592
|
+
axiosMock.mockRejectedValue(
|
|
593
|
+
new Error("connect ECONNREFUSED 93.184.216.34:443"),
|
|
594
|
+
);
|
|
595
|
+
const error: Error = await captureRejection(
|
|
596
|
+
DataSourceHttpFetch.fetch(makeRequest()),
|
|
597
|
+
);
|
|
598
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
599
|
+
expect(error.message).toContain("Could not reach data source");
|
|
600
|
+
expect(error.message).toContain("Connection Refused");
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
test("an axios timeout without a response maps to a friendly timeout error", async () => {
|
|
604
|
+
axiosMock.mockRejectedValue({
|
|
605
|
+
isAxiosError: true,
|
|
606
|
+
message: "timeout of 30000ms exceeded",
|
|
607
|
+
response: undefined,
|
|
608
|
+
});
|
|
609
|
+
const error: Error = await captureRejection(
|
|
610
|
+
DataSourceHttpFetch.fetch(makeRequest()),
|
|
611
|
+
);
|
|
612
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
613
|
+
expect(error.message).toContain("Could not reach data source");
|
|
614
|
+
expect(error.message).toContain("Timeout Error");
|
|
615
|
+
});
|
|
616
|
+
});
|