From d3de57370aea8227ac2dd85a80003d37ec213ca0 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Wed, 14 Feb 2024 14:12:01 -0800 Subject: [PATCH 01/26] update snapshots Signed-off-by: Jackie Han --- .../__snapshots__/AnomalyStat.test.tsx.snap | 12 +++- .../AnomalyHeatmapChart.test.tsx.snap | 36 ++++++++-- .../AggregationSelector.test.tsx.snap | 12 +++- .../__snapshots__/CategoryField.test.tsx.snap | 18 ++++- .../ConfigureModel.test.tsx.snap | 66 +++++++++++++++---- .../EmptyDashboard.test.tsx.snap | 6 +- .../__snapshots__/IndexOption.test.tsx.snap | 6 +- .../__snapshots__/Settings.test.tsx.snap | 12 +++- .../DefineDetector.test.tsx.snap | 42 ++++++++++-- .../DetectorConfig.test.tsx.snap | 18 ++++- .../DetectorControls.test.tsx.snap | 6 +- .../MonitorCallout.test.tsx.snap | 12 +++- .../__snapshots__/HistoricalJob.test.tsx.snap | 6 +- .../__snapshots__/RealTimeJob.test.tsx.snap | 6 +- .../__snapshots__/DetectorJobs.test.tsx.snap | 12 +++- .../__snapshots__/ListControls.test.tsx.snap | 12 +++- .../__snapshots__/ListFilters.test.tsx.snap | 30 +++++++-- ...ptyHistoricalDetectorResults.test.tsx.snap | 6 +- .../__snapshots__/SampleDataBox.test.tsx.snap | 6 +- .../SampleDataCallout.test.tsx.snap | 12 +++- .../AnomalyDetectionOverview.test.tsx.snap | 48 +++++++++++--- .../ModelConfigurationFields.test.tsx.snap | 18 ++++- .../ReviewAndCreate.test.tsx.snap | 12 +++- 23 files changed, 345 insertions(+), 69 deletions(-) diff --git a/public/pages/AnomalyCharts/components/AnomaliesStat/__tests__/__snapshots__/AnomalyStat.test.tsx.snap b/public/pages/AnomalyCharts/components/AnomaliesStat/__tests__/__snapshots__/AnomalyStat.test.tsx.snap index 65ee1883..4d0a9440 100644 --- a/public/pages/AnomalyCharts/components/AnomaliesStat/__tests__/__snapshots__/AnomalyStat.test.tsx.snap +++ b/public/pages/AnomalyCharts/components/AnomaliesStat/__tests__/__snapshots__/AnomalyStat.test.tsx.snap @@ -113,7 +113,11 @@ exports[` spec Alert Stat renders component with monitor and not l viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -282,7 +286,11 @@ exports[` spec Anomaly Stat with tooltip renders compo viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + +

diff --git a/public/pages/AnomalyCharts/containers/__tests__/__snapshots__/AnomalyHeatmapChart.test.tsx.snap b/public/pages/AnomalyCharts/containers/__tests__/__snapshots__/AnomalyHeatmapChart.test.tsx.snap index 7cc82915..4517ab41 100644 --- a/public/pages/AnomalyCharts/containers/__tests__/__snapshots__/AnomalyHeatmapChart.test.tsx.snap +++ b/public/pages/AnomalyCharts/containers/__tests__/__snapshots__/AnomalyHeatmapChart.test.tsx.snap @@ -24,7 +24,11 @@ exports[` spec AnomalyHeatmapChart with Sample anomaly da viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -128,7 +132,11 @@ exports[` spec AnomalyHeatmapChart with Sample anomaly da viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -166,7 +174,11 @@ exports[` spec AnomalyHeatmapChart with Sample anomaly da viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -240,7 +256,11 @@ exports[` spec AnomalyHeatmapChart with Sample anomaly da viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -292,7 +312,11 @@ exports[` spec AnomalyHeatmapChart with Sample anomaly da viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/ConfigureModel/components/AggregationSelector/__tests__/__snapshots__/AggregationSelector.test.tsx.snap b/public/pages/ConfigureModel/components/AggregationSelector/__tests__/__snapshots__/AggregationSelector.test.tsx.snap index b82301bc..18303391 100644 --- a/public/pages/ConfigureModel/components/AggregationSelector/__tests__/__snapshots__/AggregationSelector.test.tsx.snap +++ b/public/pages/ConfigureModel/components/AggregationSelector/__tests__/__snapshots__/AggregationSelector.test.tsx.snap @@ -74,7 +74,11 @@ exports[` spec Empty results renders component with aggre viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -164,7 +168,11 @@ exports[` spec Empty results renders component with aggre viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/ConfigureModel/components/CategoryField/__tests__/__snapshots__/CategoryField.test.tsx.snap b/public/pages/ConfigureModel/components/CategoryField/__tests__/__snapshots__/CategoryField.test.tsx.snap index df15a8a6..8c0d619a 100644 --- a/public/pages/ConfigureModel/components/CategoryField/__tests__/__snapshots__/CategoryField.test.tsx.snap +++ b/public/pages/ConfigureModel/components/CategoryField/__tests__/__snapshots__/CategoryField.test.tsx.snap @@ -61,7 +61,11 @@ exports[` spec renders the component when disabled 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -263,7 +267,11 @@ exports[` spec renders the component when enabled 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -352,7 +360,11 @@ exports[` spec renders the component when enabled 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/ConfigureModel/containers/__tests__/__snapshots__/ConfigureModel.test.tsx.snap b/public/pages/ConfigureModel/containers/__tests__/__snapshots__/ConfigureModel.test.tsx.snap index 365b6302..a7695db8 100644 --- a/public/pages/ConfigureModel/containers/__tests__/__snapshots__/ConfigureModel.test.tsx.snap +++ b/public/pages/ConfigureModel/containers/__tests__/__snapshots__/ConfigureModel.test.tsx.snap @@ -46,7 +46,11 @@ exports[` spec creating model configuration renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -106,7 +110,11 @@ exports[` spec creating model configuration renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -165,7 +173,11 @@ exports[` spec creating model configuration renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + spec creating model configuration renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -438,7 +454,11 @@ exports[` spec creating model configuration renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -528,7 +548,11 @@ exports[` spec creating model configuration renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -638,7 +662,11 @@ exports[` spec creating model configuration renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -684,7 +712,11 @@ exports[` spec creating model configuration renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -854,7 +886,11 @@ exports[` spec creating model configuration renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -895,7 +931,11 @@ exports[` spec creating model configuration renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -1686,7 +1726,11 @@ exports[` spec editing model configuration renders the compone viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/Dashboard/Components/EmptyDashboard/__tests__/__snapshots__/EmptyDashboard.test.tsx.snap b/public/pages/Dashboard/Components/EmptyDashboard/__tests__/__snapshots__/EmptyDashboard.test.tsx.snap index 03d27a8e..67762b0d 100644 --- a/public/pages/Dashboard/Components/EmptyDashboard/__tests__/__snapshots__/EmptyDashboard.test.tsx.snap +++ b/public/pages/Dashboard/Components/EmptyDashboard/__tests__/__snapshots__/EmptyDashboard.test.tsx.snap @@ -45,7 +45,11 @@ exports[` spec Empty results renders component with empty messa viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/DefineDetector/components/Datasource/__tests__/__snapshots__/IndexOption.test.tsx.snap b/public/pages/DefineDetector/components/Datasource/__tests__/__snapshots__/IndexOption.test.tsx.snap index da9f3b91..b6546aba 100644 --- a/public/pages/DefineDetector/components/Datasource/__tests__/__snapshots__/IndexOption.test.tsx.snap +++ b/public/pages/DefineDetector/components/Datasource/__tests__/__snapshots__/IndexOption.test.tsx.snap @@ -20,7 +20,11 @@ exports[` spec renders the component 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + +
spec renders the component 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -203,7 +207,11 @@ exports[` spec renders the component 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/DefineDetector/containers/__tests__/__snapshots__/DefineDetector.test.tsx.snap b/public/pages/DefineDetector/containers/__tests__/__snapshots__/DefineDetector.test.tsx.snap index 8ccaa0d1..7c86c166 100644 --- a/public/pages/DefineDetector/containers/__tests__/__snapshots__/DefineDetector.test.tsx.snap +++ b/public/pages/DefineDetector/containers/__tests__/__snapshots__/DefineDetector.test.tsx.snap @@ -323,7 +323,11 @@ exports[` Full creating detector definition renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + +
@@ -392,7 +396,11 @@ exports[` Full creating detector definition renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + +
Full creating detector definition renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + +
@@ -671,7 +683,11 @@ exports[` Full creating detector definition renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -775,7 +791,11 @@ exports[` Full creating detector definition renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -892,7 +912,11 @@ exports[` Full creating detector definition renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -2174,7 +2198,11 @@ exports[` empty editing detector definition renders the compon viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/DetectorConfig/containers/__tests__/__snapshots__/DetectorConfig.test.tsx.snap b/public/pages/DetectorConfig/containers/__tests__/__snapshots__/DetectorConfig.test.tsx.snap index 9ddc5f82..6580e900 100644 --- a/public/pages/DetectorConfig/containers/__tests__/__snapshots__/DetectorConfig.test.tsx.snap +++ b/public/pages/DetectorConfig/containers/__tests__/__snapshots__/DetectorConfig.test.tsx.snap @@ -606,7 +606,11 @@ exports[` spec renders the component 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -661,7 +665,11 @@ exports[` spec renders the component 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -1104,7 +1112,11 @@ exports[` spec renders the component 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + +
spec Detector Controls renders detector controls w viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/DetectorDetail/components/MonitorCallout/__tests__/__snapshots__/MonitorCallout.test.tsx.snap b/public/pages/DetectorDetail/components/MonitorCallout/__tests__/__snapshots__/MonitorCallout.test.tsx.snap index e9ff894d..44b14d8c 100644 --- a/public/pages/DetectorDetail/components/MonitorCallout/__tests__/__snapshots__/MonitorCallout.test.tsx.snap +++ b/public/pages/DetectorDetail/components/MonitorCallout/__tests__/__snapshots__/MonitorCallout.test.tsx.snap @@ -16,7 +16,11 @@ exports[` spec Monitor callout renders component 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -49,7 +53,11 @@ exports[` spec Monitor callout renders component 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/DetectorJobs/components/HistoricalJob/__tests__/__snapshots__/HistoricalJob.test.tsx.snap b/public/pages/DetectorJobs/components/HistoricalJob/__tests__/__snapshots__/HistoricalJob.test.tsx.snap index e5970a49..9c83d67e 100644 --- a/public/pages/DetectorJobs/components/HistoricalJob/__tests__/__snapshots__/HistoricalJob.test.tsx.snap +++ b/public/pages/DetectorJobs/components/HistoricalJob/__tests__/__snapshots__/HistoricalJob.test.tsx.snap @@ -49,7 +49,11 @@ exports[` spec renders the component 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/DetectorJobs/components/RealTimeJob/__tests__/__snapshots__/RealTimeJob.test.tsx.snap b/public/pages/DetectorJobs/components/RealTimeJob/__tests__/__snapshots__/RealTimeJob.test.tsx.snap index f8391adb..0c727c43 100644 --- a/public/pages/DetectorJobs/components/RealTimeJob/__tests__/__snapshots__/RealTimeJob.test.tsx.snap +++ b/public/pages/DetectorJobs/components/RealTimeJob/__tests__/__snapshots__/RealTimeJob.test.tsx.snap @@ -49,7 +49,11 @@ exports[` spec renders the component 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/DetectorJobs/containers/__tests__/__snapshots__/DetectorJobs.test.tsx.snap b/public/pages/DetectorJobs/containers/__tests__/__snapshots__/DetectorJobs.test.tsx.snap index a40c033e..89274801 100644 --- a/public/pages/DetectorJobs/containers/__tests__/__snapshots__/DetectorJobs.test.tsx.snap +++ b/public/pages/DetectorJobs/containers/__tests__/__snapshots__/DetectorJobs.test.tsx.snap @@ -69,7 +69,11 @@ exports[` spec configuring detector jobs renders the component 1 viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -175,7 +179,11 @@ exports[` spec configuring detector jobs renders the component 1 viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/DetectorResults/components/ListControls/__tests__/__snapshots__/ListControls.test.tsx.snap b/public/pages/DetectorResults/components/ListControls/__tests__/__snapshots__/ListControls.test.tsx.snap index 0962ac71..cdae893b 100644 --- a/public/pages/DetectorResults/components/ListControls/__tests__/__snapshots__/ListControls.test.tsx.snap +++ b/public/pages/DetectorResults/components/ListControls/__tests__/__snapshots__/ListControls.test.tsx.snap @@ -28,7 +28,11 @@ exports[` spec Empty results renders component with empty messag viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + +
    spec Empty results renders component with empty messag viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + +
diff --git a/public/pages/DetectorsList/components/ListFilters/__tests__/__snapshots__/ListFilters.test.tsx.snap b/public/pages/DetectorsList/components/ListFilters/__tests__/__snapshots__/ListFilters.test.tsx.snap index 6b517bd1..cb793711 100644 --- a/public/pages/DetectorsList/components/ListFilters/__tests__/__snapshots__/ListFilters.test.tsx.snap +++ b/public/pages/DetectorsList/components/ListFilters/__tests__/__snapshots__/ListFilters.test.tsx.snap @@ -36,7 +36,11 @@ exports[` spec Empty results renders component with empty message viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + +
@@ -103,7 +107,11 @@ exports[` spec Empty results renders component with empty message viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -171,7 +179,11 @@ exports[` spec Empty results renders component with empty message viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -201,7 +213,11 @@ exports[` spec Empty results renders component with empty message viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + +
    spec Empty results renders component with empty message viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/HistoricalDetectorResults/components/__tests__/__snapshots__/EmptyHistoricalDetectorResults.test.tsx.snap b/public/pages/HistoricalDetectorResults/components/__tests__/__snapshots__/EmptyHistoricalDetectorResults.test.tsx.snap index 0a555922..fbec394e 100644 --- a/public/pages/HistoricalDetectorResults/components/__tests__/__snapshots__/EmptyHistoricalDetectorResults.test.tsx.snap +++ b/public/pages/HistoricalDetectorResults/components/__tests__/__snapshots__/EmptyHistoricalDetectorResults.test.tsx.snap @@ -39,7 +39,11 @@ exports[` spec renders component 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/Overview/components/SampleDataBox/__tests__/__snapshots__/SampleDataBox.test.tsx.snap b/public/pages/Overview/components/SampleDataBox/__tests__/__snapshots__/SampleDataBox.test.tsx.snap index 20c5ca11..fbec5767 100644 --- a/public/pages/Overview/components/SampleDataBox/__tests__/__snapshots__/SampleDataBox.test.tsx.snap +++ b/public/pages/Overview/components/SampleDataBox/__tests__/__snapshots__/SampleDataBox.test.tsx.snap @@ -255,7 +255,11 @@ exports[` spec Data not loaded renders component 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + +

    spec Data not loaded renders component 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -49,7 +53,11 @@ exports[` spec Data not loaded renders component 1`] = ` viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/Overview/containers/__tests__/__snapshots__/AnomalyDetectionOverview.test.tsx.snap b/public/pages/Overview/containers/__tests__/__snapshots__/AnomalyDetectionOverview.test.tsx.snap index 26092ab6..1cdefe53 100644 --- a/public/pages/Overview/containers/__tests__/__snapshots__/AnomalyDetectionOverview.test.tsx.snap +++ b/public/pages/Overview/containers/__tests__/__snapshots__/AnomalyDetectionOverview.test.tsx.snap @@ -62,7 +62,11 @@ exports[` spec No sample detectors created renders c viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -165,7 +169,11 @@ exports[` spec No sample detectors created renders c viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -227,7 +235,11 @@ exports[` spec No sample detectors created renders c viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -289,7 +301,11 @@ exports[` spec No sample detectors created renders c viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -351,7 +367,11 @@ exports[` spec No sample detectors created renders c viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -452,7 +472,11 @@ exports[` spec No sample detectors created renders c viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + +

    spec No sample detectors created renders c viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + +

    spec No sample detectors created renders c viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + +

    + > + + @@ -305,7 +309,11 @@ exports[`ModelConfigurationFields renders the component in create mode (no ID) 1 viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -360,7 +368,11 @@ exports[`ModelConfigurationFields renders the component in create mode (no ID) 1 viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + diff --git a/public/pages/ReviewAndCreate/containers/__tests__/__snapshots__/ReviewAndCreate.test.tsx.snap b/public/pages/ReviewAndCreate/containers/__tests__/__snapshots__/ReviewAndCreate.test.tsx.snap index ece4b589..7f09ec44 100644 --- a/public/pages/ReviewAndCreate/containers/__tests__/__snapshots__/ReviewAndCreate.test.tsx.snap +++ b/public/pages/ReviewAndCreate/containers/__tests__/__snapshots__/ReviewAndCreate.test.tsx.snap @@ -713,7 +713,11 @@ exports[` spec renders the component, validation loading 1`] viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + @@ -768,7 +772,11 @@ exports[` spec renders the component, validation loading 1`] viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + From 8258d5cd01595380eb82b8dbf4329c6a0dc60c82 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Wed, 14 Feb 2024 14:36:00 -0800 Subject: [PATCH 02/26] add snpshot Signed-off-by: Jackie Han --- .../__tests__/__snapshots__/ListActions.test.tsx.snap | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/pages/DetectorsList/components/ListActions/__tests__/__snapshots__/ListActions.test.tsx.snap b/public/pages/DetectorsList/components/ListActions/__tests__/__snapshots__/ListActions.test.tsx.snap index 959774fa..180cdf14 100644 --- a/public/pages/DetectorsList/components/ListActions/__tests__/__snapshots__/ListActions.test.tsx.snap +++ b/public/pages/DetectorsList/components/ListActions/__tests__/__snapshots__/ListActions.test.tsx.snap @@ -33,7 +33,11 @@ exports[` spec List actions renders component when disabled 1`] = viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" - /> + > + + From 9f62cf2190d4b28ea690598e6b92536839197e99 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Mon, 26 Feb 2024 13:42:03 -0800 Subject: [PATCH 03/26] add data source client Signed-off-by: Jackie Han --- opensearch_dashboards.json | 6 ++- package.json | 4 +- .../containers/DefineDetector.tsx | 19 ++++++++ public/plugin.ts | 2 + public/services.ts | 4 ++ server/plugin.ts | 46 +++++++++++++++---- server/routes/ad.ts | 29 ++++++++---- 7 files changed, 88 insertions(+), 22 deletions(-) diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index 72a48458..55581d80 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -1,10 +1,12 @@ { "id": "anomalyDetectionDashboards", - "version": "3.0.0.0", - "opensearchDashboardsVersion": "3.0.0", + "version": "2.11.0.0", + "opensearchDashboardsVersion": "2.11:.0", "configPath": [ "anomaly_detection_dashboards" ], + "optionalPlugins": ["dataSource"], + "requiredBundles": ["dataSourceManagement"], "requiredPlugins": [ "opensearchDashboardsUtils", "expressions", diff --git a/package.json b/package.json index 27a2118b..b6916c6b 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "anomaly-detection-dashboards", - "version": "3.0.0.0", + "version": "2.11.0.0", "description": "OpenSearch Anomaly Detection Dashboards Plugin", "main": "index.js", "config": { - "plugin_version": "3.0.0.0", + "plugin_version": "2.11.0.0", "plugin_name": "anomalyDetectionDashboards", "plugin_zip_name": "anomaly-detection-dashboards" }, diff --git a/public/pages/DefineDetector/containers/DefineDetector.tsx b/public/pages/DefineDetector/containers/DefineDetector.tsx index 459553c0..93b75e2b 100644 --- a/public/pages/DefineDetector/containers/DefineDetector.tsx +++ b/public/pages/DefineDetector/containers/DefineDetector.tsx @@ -50,6 +50,8 @@ import { Detector, FILTER_TYPES } from '../../../models/interfaces'; import { prettifyErrorMessage } from '../../../../server/utils/helpers'; import { DETECTOR_STATE } from '../../../../server/utils/constants'; import { ModelConfigurationFormikValues } from 'public/pages/ConfigureModel/models/interfaces'; +import { getNotifications, getSavedObjectsClient } from '../../../services'; +import { ClusterSelector } from '../../../../../../src/plugins/data_source_management/public'; interface DefineDetectorRouterProps { detectorId?: string; @@ -71,6 +73,7 @@ export const DefineDetector = (props: DefineDetectorProps) => { const detectorId: string = get(props, 'match.params.detectorId', ''); const { detector, hasError } = useFetchDetectorInfo(detectorId); const [newIndexSelected, setNewIndexSelected] = useState(false); + const [selectedDataSource, setSelectedDataSource] = useState(); // To handle backward compatibility, we need to pass some fields via // props to the subcomponents so they can render correctly @@ -90,6 +93,12 @@ export const DefineDetector = (props: DefineDetectorProps) => { ) as FILTER_TYPES; const oldFilterQuery = get(detector, 'filterQuery', undefined); + const onSelectedDataSource = (e) => { + const dataConnectionId = e[0] ? e[0].id : undefined; + setSelectedDataSource(dataConnectionId); + console.log(dataConnectionId); + } + // Jump to top of page on first load useEffect(() => { scroll(0, 0); @@ -249,10 +258,20 @@ export const DefineDetector = (props: DefineDetectorProps) => { + + + ('Query'); +export const [getSavedObjectsClient, setSavedObjectsClient] = +createGetterSetter('SavedObjectsClient'); + // This is primarily used for mocking this module and each of its fns in tests. export default { getSavedFeatureAnywhereLoader, @@ -49,4 +52,5 @@ export default { getOverlays, setUISettings, setQueryService, + getSavedObjectsClient }; diff --git a/server/plugin.ts b/server/plugin.ts index 88a573cb..91d4461d 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -35,6 +35,13 @@ import SampleDataService, { registerSampleDataRoutes, } from './routes/sampleData'; import { DEFAULT_HEADERS } from './utils/constants'; +import { DataSourcePluginSetup } from '../../../src/plugins/data_source/server/types'; +import { DataSourceManagementPlugin } from '../../../src/plugins/data_source_management/public'; + +export interface ADPluginSetupDependencies { + dataSourceManagement: ReturnType; + dataSource: DataSourcePluginSetup; +} export class AnomalyDetectionOpenSearchDashboardsPlugin implements @@ -50,20 +57,39 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin this.logger = initializerContext.logger.get(); this.globalConfig$ = initializerContext.config.legacy.globalConfig$; } - public async setup(core: CoreSetup) { + public async setup(core: CoreSetup, {dataSource} : ADPluginSetupDependencies) { // Get any custom/overridden headers const globalConfig = await this.globalConfig$.pipe(first()).toPromise(); const { customHeaders, ...rest } = globalConfig.opensearch; - // Create OpenSearch client w/ relevant plugins and headers - const client: ILegacyClusterClient = core.opensearch.legacy.createClient( - 'anomaly_detection', - { - plugins: [adPlugin, alertingPlugin], - customHeaders: { ...customHeaders, ...DEFAULT_HEADERS }, - ...rest, - } - ); + const dataSourceEnabled = !!dataSource; + + let client: ILegacyClusterClient | undefined = undefined; + + if (!dataSourceEnabled) { + client = core.opensearch.legacy.createClient( + 'anomaly_detection', + { + plugins: [adPlugin, alertingPlugin], + customHeaders: { ...customHeaders, ...DEFAULT_HEADERS }, + ...rest, + } + ) + } else { + dataSource.registerCustomApiSchema(adPlugin) + dataSource.registerCustomApiSchema(alertingPlugin) + + } + + // // Create OpenSearch client w/ relevant plugins and headers + // const client: ILegacyClusterClient = core.opensearch.legacy.createClient( + // 'anomaly_detection', + // { + // plugins: [adPlugin, alertingPlugin], + // customHeaders: { ...customHeaders, ...DEFAULT_HEADERS }, + // ...rest, + // } + // ); // Create router const apiRouter: Router = createRouter( diff --git a/server/routes/ad.ts b/server/routes/ad.ts index 4c9eb54e..649887e5 100644 --- a/server/routes/ad.ts +++ b/server/routes/ad.ts @@ -198,11 +198,17 @@ export default class AdService { .asScoped(request) .callAsCurrentUser('ad.updateDetector', params); } else { - response = await this.client - .asScoped(request) - .callAsCurrentUser('ad.createDetector', { + const client = context.dataSource.opensearch.legacy.getClient('4585f560-d1ef-11ee-aa63-2181676cc573'); + + response = await client + .callAPI('ad.createDetector', { body: params.body, }); + // response = await this.client + // .asScoped(request) + // .callAsCurrentUser('ad.createDetector', { + // body: params.body, + // }); } const resp = { ...response.anomaly_detector, @@ -1080,13 +1086,20 @@ export default class AdService { request: OpenSearchDashboardsRequest, opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { + console.log("here") try { const { detectorName } = request.params as { detectorName: string }; - const response = await this.client - .asScoped(request) - .callAsCurrentUser('ad.matchDetector', { - detectorName, - }); + const client = context.dataSource.opensearch.legacy.getClient('4585f560-d1ef-11ee-aa63-2181676cc573'); + + const response = await client + .callAPI('ad.matchDetector', { + detectorName + }); + // const response = await this.client + // .asScoped(request) + // .callAsCurrentUser('ad.matchDetector', { + // detectorName, + // }); return opensearchDashboardsResponse.ok({ body: { ok: true, From b0d3f5b90d0164e02ec3fbae1e68946523dfc9a5 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Thu, 29 Feb 2024 11:20:44 -0800 Subject: [PATCH 04/26] test --- .../components/Datasource/DataSource.tsx | 25 +++++++++++++++++++ .../NameAndDescription/NameAndDescription.tsx | 13 ++++++++++ .../containers/DefineDetector.tsx | 19 +------------- .../components/ListFilters/ListFilters.tsx | 20 ++++++++++++++- .../DetectorsList/containers/List/List.tsx | 9 +++++++ server/routes/ad.ts | 11 ++++++-- 6 files changed, 76 insertions(+), 21 deletions(-) diff --git a/public/pages/DefineDetector/components/Datasource/DataSource.tsx b/public/pages/DefineDetector/components/Datasource/DataSource.tsx index dd268dde..7863a305 100644 --- a/public/pages/DefineDetector/components/Datasource/DataSource.tsx +++ b/public/pages/DefineDetector/components/Datasource/DataSource.tsx @@ -32,6 +32,9 @@ import { DetectorDefinitionFormikValues } from '../../models/interfaces'; import { ModelConfigurationFormikValues } from '../../../ConfigureModel/models/interfaces'; import { INITIAL_MODEL_CONFIGURATION_VALUES } from '../../../ConfigureModel/utils/constants'; import { FILTER_TYPES } from '../../../../models/interfaces'; +import { getNotifications, getSavedObjectsClient } from '../../../../services'; +import { ClusterSelector } from '../../../../../../../src/plugins/data_source_management/public'; + interface DataSourceProps { formikProps: FormikProps; @@ -44,6 +47,13 @@ interface DataSourceProps { } export function DataSource(props: DataSourceProps) { + const [selectedDataSource, setSelectedDataSource] = useState(); + + const onSelectedDataSource = (e) => { + const dataConnectionId = e[0] ? e[0].id : undefined; + setSelectedDataSource(dataConnectionId); + console.log(dataConnectionId); + } const dispatch = useDispatch(); const [indexName, setIndexName] = useState( props.formikProps.values.index[0]?.label @@ -95,6 +105,19 @@ export function DataSource(props: DataSourceProps) { return ( + + + + + {props.isEdit && isDifferentIndex() ? (
    ) : null} + {({ field, form }: FieldProps) => { return ( + Promise; } + + function NameAndDescription(props: NameAndDescriptionProps) { + const [selectedDataSource, setSelectedDataSource] = useState(); + + const onSelectedDataSource = (e) => { + const dataConnectionId = e[0] ? e[0].id : undefined; + setSelectedDataSource(dataConnectionId); + console.log(dataConnectionId); + } return ( @@ -44,6 +56,7 @@ function NameAndDescription(props: NameAndDescriptionProps) { )} + {({ field, form }: FieldProps) => ( { const detectorId: string = get(props, 'match.params.detectorId', ''); const { detector, hasError } = useFetchDetectorInfo(detectorId); const [newIndexSelected, setNewIndexSelected] = useState(false); - const [selectedDataSource, setSelectedDataSource] = useState(); // To handle backward compatibility, we need to pass some fields via // props to the subcomponents so they can render correctly @@ -93,12 +90,6 @@ export const DefineDetector = (props: DefineDetectorProps) => { ) as FILTER_TYPES; const oldFilterQuery = get(detector, 'filterQuery', undefined); - const onSelectedDataSource = (e) => { - const dataConnectionId = e[0] ? e[0].id : undefined; - setSelectedDataSource(dataConnectionId); - console.log(dataConnectionId); - } - // Jump to top of page on first load useEffect(() => { scroll(0, 0); @@ -263,15 +254,7 @@ export const DefineDetector = (props: DefineDetectorProps) => { onValidateDetectorName={handleValidateName} /> - - + void; onIndexChange: (options: EuiComboBoxOptionProps[]) => void; + onDataSourceChange: (e: any) => void; onSearchDetectorChange: (e: React.ChangeEvent) => void; onSearchIndexChange: (searchValue: string) => void; onPageClick: (pageNumber: number) => void; } export const ListFilters = (props: ListFiltersProps) => ( + ( fullWidth={true} /> + + + + + ( fullWidth={true} /> + + {props.pageCount > 1 ? ( { }); }; + const [selectedDataSource, setSelectedDataSource] = useState(); + + const handleDataSourceChange = (e) => { + const dataConnectionId = e[0] ? e[0].id : undefined; + setSelectedDataSource(dataConnectionId); + console.log(dataConnectionId); + } + const handleResetFilter = () => { setState((state) => ({ ...state, @@ -668,6 +676,7 @@ export const DetectorList = (props: ListProps) => { indexOptions={indexOptions} onDetectorStateChange={handleDetectorStateChange} onIndexChange={handleIndexChange} + onDataSourceChange={handleDataSourceChange} onSearchDetectorChange={handleSearchDetectorChange} onSearchIndexChange={handleSearchIndexChange} onPageClick={handlePageChange} diff --git a/server/routes/ad.ts b/server/routes/ad.ts index 649887e5..5241c436 100644 --- a/server/routes/ad.ts +++ b/server/routes/ad.ts @@ -245,12 +245,19 @@ export default class AdService { const requestBody = JSON.stringify( convertPreviewInputKeysToSnakeCase(request.body) ); + const client = context.dataSource.opensearch.legacy.getClient('4585f560-d1ef-11ee-aa63-2181676cc573'); + const response = await this.client - .asScoped(request) - .callAsCurrentUser('ad.validateDetector', { + .callAPI('ad.validateDetector', { body: requestBody, validationType: validationType, }); + // const response = await this.client + // .asScoped(request) + // .callAsCurrentUser('ad.validateDetector', { + // body: requestBody, + // validationType: validationType, + // }); return opensearchDashboardsResponse.ok({ body: { ok: true, From e0333ec08151636fdbcee7ad44b9f5eb1ceae4a7 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Thu, 4 Apr 2024 15:36:41 -0700 Subject: [PATCH 05/26] neo List Detectors page Signed-off-by: Jackie Han --- public/anomaly_detection_app.tsx | 17 ++++- public/models/interfaces.ts | 1 + .../DetectorsList/containers/List/List.tsx | 67 +++++++++++++++++-- public/pages/main/Main.tsx | 19 +++++- public/pages/utils/helpers.ts | 20 +++++- public/plugin.ts | 11 ++- public/services.ts | 4 ++ server/models/types.ts | 1 + server/plugin.ts | 31 ++++++--- server/routes/ad.ts | 45 +++++++------ server/utils/helpers.ts | 24 +++++++ 11 files changed, 198 insertions(+), 42 deletions(-) diff --git a/public/anomaly_detection_app.tsx b/public/anomaly_detection_app.tsx index dfbc4591..87b11cfb 100644 --- a/public/anomaly_detection_app.tsx +++ b/public/anomaly_detection_app.tsx @@ -17,8 +17,15 @@ import { Main } from './pages/main'; import { Provider } from 'react-redux'; import configureStore from './redux/configureStore'; import { CoreServicesContext } from './components/CoreServices/CoreServices'; +import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public'; +import { DataSourcePluginSetup } from '../../../src/plugins/data_source/public'; -export function renderApp(coreStart: CoreStart, params: AppMountParameters) { +export function renderApp( + coreStart: CoreStart, + params: AppMountParameters, + dataSourceManagement: DataSourceManagementPluginSetup, + dataSource: DataSourcePluginSetup +) { const http = coreStart.http; const store = configureStore(http); @@ -29,13 +36,19 @@ export function renderApp(coreStart: CoreStart, params: AppMountParameters) { } else { require('@elastic/charts/dist/theme_only_light.css'); } + ReactDOM.render( ( -
    +
    )} /> diff --git a/public/models/interfaces.ts b/public/models/interfaces.ts index eff5ead5..ecce16ef 100644 --- a/public/models/interfaces.ts +++ b/public/models/interfaces.ts @@ -218,6 +218,7 @@ export type DetectorListItem = { lastUpdateTime: number; enabledTime?: number; detectorType?: string; + dataSourceId: string; }; export type EntityData = { diff --git a/public/pages/DetectorsList/containers/List/List.tsx b/public/pages/DetectorsList/containers/List/List.tsx index 864f9951..ab37fc94 100644 --- a/public/pages/DetectorsList/containers/List/List.tsx +++ b/public/pages/DetectorsList/containers/List/List.tsx @@ -20,7 +20,7 @@ import { } from '@elastic/eui'; import { debounce, get, isEmpty } from 'lodash'; import queryString from 'querystring'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { RouteComponentProps } from 'react-router'; import { @@ -44,13 +44,16 @@ import { } from '../../../../redux/reducers/opensearch'; import { APP_PATH, PLUGIN_NAME } from '../../../../utils/constants'; import { DETECTOR_STATE } from '../../../../../server/utils/constants'; -import { getVisibleOptions, sanitizeSearchText } from '../../../utils/helpers'; +import { + getAllDetectorsQueryParamsWithDataSourceId, + getVisibleOptions, + sanitizeSearchText, +} from '../../../utils/helpers'; import { EmptyDetectorMessage } from '../../components/EmptyMessage/EmptyMessage'; import { ListFilters } from '../../components/ListFilters/ListFilters'; import { MAX_DETECTORS, MAX_SELECTED_INDICES, - GET_ALL_DETECTORS_QUERY_PARAMS, ALL_DETECTOR_STATES, ALL_INDICES, SINGLE_DETECTOR_NOT_FOUND_MSG, @@ -78,8 +81,13 @@ import { NO_PERMISSIONS_KEY_WORD, prettifyErrorMessage, } from '../../../../../server/utils/helpers'; -import { CoreStart } from '../../../../../../../src/core/public'; +import { CoreStart, MountPoint } from '../../../../../../../src/core/public'; import { CoreServicesContext } from '../../../../components/CoreServices/CoreServices'; +import { + DataSourceManagementPluginSetup, + DataSourceSelectableConfig, +} from '../../../../../../../src/plugins/data_source_management/public'; +import { getNotifications, getSavedObjectsClient } from '../../../../services'; export interface ListRouterParams { from: string; @@ -88,13 +96,19 @@ export interface ListRouterParams { indices: string; sortDirection: SORT_DIRECTION; sortField: string; + dataSourceId: string; +} +interface ListProps extends RouteComponentProps { + dataSourceEnabled: boolean; + dataSourceManagement: DataSourceManagementPluginSetup; + setActionMenu: (menuMount: MountPoint | undefined) => void; } -interface ListProps extends RouteComponentProps {} interface ListState { page: number; queryParams: GetDetectorsQueryParams; selectedDetectorStates: DETECTOR_STATE[]; selectedIndices: string[]; + selectedDataSourceId: string; } interface ConfirmModalState { isOpen: boolean; @@ -198,6 +212,9 @@ export const DetectorList = (props: ListProps) => { selectedIndices: queryParams.indices ? queryParams.indices.split(',') : ALL_INDICES, + selectedDataSourceId: queryParams.dataSourceId + ? queryParams.dataSourceId + : '', }); // Set breadcrumbs on page initialization @@ -215,6 +232,7 @@ export const DetectorList = (props: ListProps) => { ...state.queryParams, indices: state.selectedIndices.join(','), from: state.page * state.queryParams.size, + dataSourceId: state.selectedDataSourceId, }; history.replace({ @@ -229,6 +247,7 @@ export const DetectorList = (props: ListProps) => { state.queryParams, state.selectedDetectorStates, state.selectedIndices, + state.selectedDataSourceId, ]); // Handle all filtering / sorting of detectors @@ -246,7 +265,8 @@ export const DetectorList = (props: ListProps) => { const curDetectorsToDisplay = getDetectorsToDisplay( curSelectedDetectors, state.page, - state.queryParams.size + state.queryParams.size, + state.selectedDataSourceId ); setDetectorsToDisplay(curDetectorsToDisplay); @@ -273,7 +293,11 @@ export const DetectorList = (props: ListProps) => { }, [confirmModalState.isRequestingToClose, isLoading]); const getUpdatedDetectors = async () => { - dispatch(getDetectorList(GET_ALL_DETECTORS_QUERY_PARAMS)); + dispatch( + getDetectorList( + getAllDetectorsQueryParamsWithDataSourceId(state.selectedDataSourceId) + ) + ); }; const handlePageChange = (pageNumber: number) => { @@ -552,6 +576,16 @@ export const DetectorList = (props: ListProps) => { }); }; + const handleDataSourceChange = (e) => { + const dataConnectionId = e[0] ? e[0].id : undefined; + + setState({ + ...state, + page: 0, + selectedDataSourceId: dataConnectionId, + }); + }; + const getConfirmModal = () => { if (confirmModalState.isOpen) { //@ts-ignore @@ -626,9 +660,28 @@ export const DetectorList = (props: ListProps) => { const confirmModal = getConfirmModal(); + const DataSourceMenu = + props.dataSourceManagement.ui.getDataSourceMenu(); + const renderDataSourceComponent = useMemo(() => { + return ( + + handleDataSourceChange(dataSources), + }} + /> + ); + }, [getSavedObjectsClient(), getNotifications(), props.setActionMenu]); + return ( + {props.dataSourceEnabled && renderDataSourceComponent} void; +} export function Main(props: MainProps) { + const { dataSourceEnabled, dataSourceManagement, setHeaderActionMenu } = + props; + const hideSideNavBar = useSelector( (state: AppState) => state.adApp.hideSideNavBar ); @@ -83,7 +91,12 @@ export function Main(props: MainProps) { exact path={APP_PATH.LIST_DETECTORS} render={(props: RouteComponentProps) => ( - + )} /> { + detectors.forEach((detector) => { + detector.dataSourceId = dataSourceId; + }); return detectors.slice(size * page, page * size + size); }; @@ -112,3 +116,15 @@ export const formatNumber = (data: any) => { return ''; } }; + +export const getAllDetectorsQueryParamsWithDataSourceId = ( + dataSourceId: string +) => ({ + from: 0, + search: '', + indices: '', + size: MAX_DETECTORS, + sortDirection: SORT_DIRECTION.ASC, + sortField: 'name', + dataSourceId: dataSourceId, +}); diff --git a/public/plugin.ts b/public/plugin.ts index 25ea0ebf..97fc1eaa 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -35,6 +35,7 @@ import { setUiActions, setUISettings, setQueryService, + setSavedObjectsClient, } from './services'; import { AnomalyDetectionOpenSearchDashboardsPluginStart } from 'public'; import { @@ -43,6 +44,7 @@ import { } from '../../../src/plugins/vis_augmenter/public'; import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; import { DataPublicPluginStart } from '../../../src/plugins/data/public'; +import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public'; declare module '../../../src/plugins/ui_actions/public' { export interface ActionContextMapping { @@ -55,6 +57,7 @@ export interface AnomalyDetectionSetupDeps { embeddable: EmbeddableSetup; notifications: NotificationsSetup; visAugmenter: VisAugmenterSetup; + dataSourceManagement: DataSourceManagementPluginSetup; //uiActions: UiActionsSetup; } @@ -82,7 +85,12 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin mount: async (params: AppMountParameters) => { const { renderApp } = await import('./anomaly_detection_app'); const [coreStart] = await core.getStartServices(); - return renderApp(coreStart, params); + return renderApp( + coreStart, + params, + plugins.dataSourceManagement, + plugins.dataSource + ); }, }); @@ -116,6 +124,7 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin setNotifications(core.notifications); setUiActions(uiActions); setQueryService(data.query); + setSavedObjectsClient(core.savedObjects.client); return {}; } } diff --git a/public/services.ts b/public/services.ts index ef899307..ec164c14 100644 --- a/public/services.ts +++ b/public/services.ts @@ -39,6 +39,9 @@ export const [getUISettings, setUISettings] = export const [getQueryService, setQueryService] = createGetterSetter('Query'); +export const [getSavedObjectsClient, setSavedObjectsClient] = + createGetterSetter('SavedObjectsClient'); + // This is primarily used for mocking this module and each of its fns in tests. export default { getSavedFeatureAnywhereLoader, @@ -49,4 +52,5 @@ export default { getOverlays, setUISettings, setQueryService, + getSavedObjectsClient, }; diff --git a/server/models/types.ts b/server/models/types.ts index 24fa7bb3..36f6443f 100644 --- a/server/models/types.ts +++ b/server/models/types.ts @@ -89,6 +89,7 @@ export type GetDetectorsQueryParams = { indices?: string; sortDirection: SORT_DIRECTION; sortField: string; + dataSourceId?: string; }; export type GetAdMonitorsQueryParams = { diff --git a/server/plugin.ts b/server/plugin.ts index 88a573cb..8c446e36 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -35,6 +35,13 @@ import SampleDataService, { registerSampleDataRoutes, } from './routes/sampleData'; import { DEFAULT_HEADERS } from './utils/constants'; +import { DataSourcePluginSetup } from '../../../src/plugins/data_source/server/types'; +import { DataSourceManagementPlugin } from '../../../src/plugins/data_source_management/public'; + +export interface ADPluginSetupDependencies { + dataSourceManagement: ReturnType; + dataSource: DataSourcePluginSetup; +} export class AnomalyDetectionOpenSearchDashboardsPlugin implements @@ -50,20 +57,28 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin this.logger = initializerContext.logger.get(); this.globalConfig$ = initializerContext.config.legacy.globalConfig$; } - public async setup(core: CoreSetup) { + public async setup( + core: CoreSetup, + { dataSource }: ADPluginSetupDependencies + ) { // Get any custom/overridden headers const globalConfig = await this.globalConfig$.pipe(first()).toPromise(); const { customHeaders, ...rest } = globalConfig.opensearch; - // Create OpenSearch client w/ relevant plugins and headers - const client: ILegacyClusterClient = core.opensearch.legacy.createClient( - 'anomaly_detection', - { + const dataSourceEnabled = !!dataSource; + + let client: ILegacyClusterClient | undefined = undefined; + + if (!dataSourceEnabled) { + client = core.opensearch.legacy.createClient('anomaly_detection', { plugins: [adPlugin, alertingPlugin], customHeaders: { ...customHeaders, ...DEFAULT_HEADERS }, ...rest, - } - ); + }); + } else { + dataSource.registerCustomApiSchema(adPlugin); + dataSource.registerCustomApiSchema(alertingPlugin); + } // Create router const apiRouter: Router = createRouter( @@ -72,7 +87,7 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin ); // Create services & register with OpenSearch client - const adService = new AdService(client); + const adService = new AdService(client, dataSourceEnabled); const alertingService = new AlertingService(client); const opensearchService = new OpenSearchService(client); const sampleDataService = new SampleDataService(client); diff --git a/server/routes/ad.ts b/server/routes/ad.ts index 4c9eb54e..9b5968dd 100644 --- a/server/routes/ad.ts +++ b/server/routes/ad.ts @@ -24,6 +24,7 @@ import { CUSTOM_AD_RESULT_INDEX_PREFIX, } from '../utils/constants'; import { + getClientBasedOnDataSource, mapKeysDeep, toCamel, toFixedNumberForAnomaly, @@ -103,9 +104,11 @@ export function registerADRoutes(apiRouter: Router, adService: AdService) { export default class AdService { private client: any; + dataSourceEnabled: boolean; - constructor(client: any) { + constructor(client: any, dataSourceEnabled: boolean) { this.client = client; + this.dataSourceEnabled = dataSourceEnabled; } deleteDetector = async ( @@ -579,6 +582,7 @@ export default class AdService { indices = '', sortDirection = SORT_DIRECTION.DESC, sortField = 'name', + dataSourceId = '', } = request.query as GetDetectorsQueryParams; const mustQueries = []; if (search.trim()) { @@ -621,10 +625,16 @@ export default class AdService { }, }, }; - const response: any = await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchDetector', { body: requestBody }); - + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + dataSourceId, + this.client + ); + const response = await callWithRequest('ad.searchDetector', { + body: requestBody, + }); const totalDetectors = get(response, 'hits.total.value', 0); //Get all detectors from search detector API @@ -655,9 +665,9 @@ export default class AdService { resultIndex: CUSTOM_AD_RESULT_INDEX_PREFIX + '*', onlyQueryCustomResultIndex: 'false', } as {}; - const aggregationResult = await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchResultsFromCustomResultIndex', { + const aggregationResult = await callWithRequest( + 'ad.searchResultsFromCustomResultIndex', + { ...requestParams, body: getResultAggregationQuery(allDetectorIds, { from, @@ -667,7 +677,8 @@ export default class AdService { search, indices, }), - }); + } + ); const aggsDetectors = get( aggregationResult, 'aggregations.unique_detectors.buckets', @@ -720,16 +731,12 @@ export default class AdService { let realtimeTasksResponse = {} as any; let historicalTasksResponse = {} as any; try { - realtimeTasksResponse = await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchTasks', { - body: getLatestDetectorTasksQuery(true), - }); - historicalTasksResponse = await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchTasks', { - body: getLatestDetectorTasksQuery(false), - }); + realtimeTasksResponse = await callWithRequest('ad.searchTasks', { + body: getLatestDetectorTasksQuery(true), + }); + historicalTasksResponse = await callWithRequest('ad.searchTasks', { + body: getLatestDetectorTasksQuery(false), + }); } catch (err) { if (!isIndexNotFoundError(err)) { throw err; diff --git a/server/utils/helpers.ts b/server/utils/helpers.ts index 15c80b3e..6233a007 100644 --- a/server/utils/helpers.ts +++ b/server/utils/helpers.ts @@ -20,6 +20,10 @@ import { } from 'lodash'; import { MIN_IN_MILLI_SECS } from './constants'; +import { + LegacyCallAPIOptions, + OpenSearchDashboardsRequest, +} from '../../../../src/core/server'; export const SHOW_DECIMAL_NUMBER_THRESHOLD = 0.01; @@ -81,3 +85,23 @@ export const prettifyErrorMessage = (rawErrorMessage: string) => { return `User ${match[2]} has no permissions to [${match[1]}].`; } }; + +export function getClientBasedOnDataSource( + context: any, + dataSourceEnabled: boolean, + request: OpenSearchDashboardsRequest, + dataSourceId: string, + client: any +): ( + endpoint: string, + clientParams?: Record, + options?: LegacyCallAPIOptions +) => any { + if (dataSourceEnabled && dataSourceId && dataSourceId.trim().length != 0) { + // client for remote cluster + return context.dataSource.opensearch.legacy.getClient(dataSourceId).callAPI; + } else { + // fall back to default local cluster + return client.asScoped(request).callAsCurrentUser; + } +} From 8a94a9b027c2ad32b3a089a3899e04e25157577d Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Thu, 4 Apr 2024 17:55:47 -0700 Subject: [PATCH 06/26] add opensearch_dashboards.json file Signed-off-by: Jackie Han --- opensearch_dashboards.json | 1 + 1 file changed, 1 insertion(+) diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index 72a48458..c62acf46 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -5,6 +5,7 @@ "configPath": [ "anomaly_detection_dashboards" ], + "optionalPlugins": ["dataSource","dataSourceManagement"], "requiredPlugins": [ "opensearchDashboardsUtils", "expressions", From f1223448ae65a571517e3ee1cdfbcee9e03492df Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Thu, 4 Apr 2024 15:36:41 -0700 Subject: [PATCH 07/26] neo List Detectors page Signed-off-by: Jackie Han --- public/anomaly_detection_app.tsx | 17 ++++- public/models/interfaces.ts | 1 + .../DetectorsList/containers/List/List.tsx | 71 ++++++++++++++++--- public/pages/main/Main.tsx | 19 ++++- public/pages/utils/helpers.ts | 20 +++++- public/plugin.ts | 9 ++- public/services.ts | 4 +- server/models/types.ts | 1 + server/plugin.ts | 35 ++++----- server/routes/ad.ts | 45 +++++++----- server/utils/helpers.ts | 24 +++++++ 11 files changed, 186 insertions(+), 60 deletions(-) diff --git a/public/anomaly_detection_app.tsx b/public/anomaly_detection_app.tsx index dfbc4591..87b11cfb 100644 --- a/public/anomaly_detection_app.tsx +++ b/public/anomaly_detection_app.tsx @@ -17,8 +17,15 @@ import { Main } from './pages/main'; import { Provider } from 'react-redux'; import configureStore from './redux/configureStore'; import { CoreServicesContext } from './components/CoreServices/CoreServices'; +import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public'; +import { DataSourcePluginSetup } from '../../../src/plugins/data_source/public'; -export function renderApp(coreStart: CoreStart, params: AppMountParameters) { +export function renderApp( + coreStart: CoreStart, + params: AppMountParameters, + dataSourceManagement: DataSourceManagementPluginSetup, + dataSource: DataSourcePluginSetup +) { const http = coreStart.http; const store = configureStore(http); @@ -29,13 +36,19 @@ export function renderApp(coreStart: CoreStart, params: AppMountParameters) { } else { require('@elastic/charts/dist/theme_only_light.css'); } + ReactDOM.render( ( -
    +
    )} /> diff --git a/public/models/interfaces.ts b/public/models/interfaces.ts index eff5ead5..ecce16ef 100644 --- a/public/models/interfaces.ts +++ b/public/models/interfaces.ts @@ -218,6 +218,7 @@ export type DetectorListItem = { lastUpdateTime: number; enabledTime?: number; detectorType?: string; + dataSourceId: string; }; export type EntityData = { diff --git a/public/pages/DetectorsList/containers/List/List.tsx b/public/pages/DetectorsList/containers/List/List.tsx index 64a97721..aeb9c56a 100644 --- a/public/pages/DetectorsList/containers/List/List.tsx +++ b/public/pages/DetectorsList/containers/List/List.tsx @@ -21,7 +21,7 @@ import { } from '@elastic/eui'; import { debounce, get, isEmpty } from 'lodash'; import queryString from 'querystring'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { RouteComponentProps } from 'react-router'; import { @@ -45,13 +45,16 @@ import { } from '../../../../redux/reducers/opensearch'; import { APP_PATH, PLUGIN_NAME } from '../../../../utils/constants'; import { DETECTOR_STATE } from '../../../../../server/utils/constants'; -import { getVisibleOptions, sanitizeSearchText } from '../../../utils/helpers'; +import { + getAllDetectorsQueryParamsWithDataSourceId, + getVisibleOptions, + sanitizeSearchText, +} from '../../../utils/helpers'; import { EmptyDetectorMessage } from '../../components/EmptyMessage/EmptyMessage'; import { ListFilters } from '../../components/ListFilters/ListFilters'; import { MAX_DETECTORS, MAX_SELECTED_INDICES, - GET_ALL_DETECTORS_QUERY_PARAMS, ALL_DETECTOR_STATES, ALL_INDICES, SINGLE_DETECTOR_NOT_FOUND_MSG, @@ -79,8 +82,13 @@ import { NO_PERMISSIONS_KEY_WORD, prettifyErrorMessage, } from '../../../../../server/utils/helpers'; -import { CoreStart } from '../../../../../../../src/core/public'; +import { CoreStart, MountPoint } from '../../../../../../../src/core/public'; import { CoreServicesContext } from '../../../../components/CoreServices/CoreServices'; +import { + DataSourceManagementPluginSetup, + DataSourceSelectableConfig, +} from '../../../../../../../src/plugins/data_source_management/public'; +import { getNotifications, getSavedObjectsClient } from '../../../../services'; export interface ListRouterParams { from: string; @@ -89,13 +97,19 @@ export interface ListRouterParams { indices: string; sortDirection: SORT_DIRECTION; sortField: string; + dataSourceId: string; +} +interface ListProps extends RouteComponentProps { + dataSourceEnabled: boolean; + dataSourceManagement: DataSourceManagementPluginSetup; + setActionMenu: (menuMount: MountPoint | undefined) => void; } -interface ListProps extends RouteComponentProps {} interface ListState { page: number; queryParams: GetDetectorsQueryParams; selectedDetectorStates: DETECTOR_STATE[]; selectedIndices: string[]; + selectedDataSourceId: string; } interface ConfirmModalState { isOpen: boolean; @@ -195,7 +209,12 @@ export const DetectorList = (props: ListProps) => { page: 0, queryParams: getURLQueryParams(props.location), selectedDetectorStates: ALL_DETECTOR_STATES, - selectedIndices: ALL_INDICES, + selectedIndices: queryParams.indices + ? queryParams.indices.split(',') + : ALL_INDICES, + selectedDataSourceId: queryParams.dataSourceId + ? queryParams.dataSourceId + : '', }); // Set breadcrumbs on page initialization @@ -213,6 +232,7 @@ export const DetectorList = (props: ListProps) => { ...state.queryParams, indices: state.selectedIndices.join(' '), from: state.page * state.queryParams.size, + dataSourceId: state.selectedDataSourceId, }; history.replace({ @@ -227,6 +247,7 @@ export const DetectorList = (props: ListProps) => { state.queryParams, state.selectedDetectorStates, state.selectedIndices, + state.selectedDataSourceId, ]); // Handle all filtering / sorting of detectors @@ -244,7 +265,8 @@ export const DetectorList = (props: ListProps) => { const curDetectorsToDisplay = getDetectorsToDisplay( curSelectedDetectors, state.page, - state.queryParams.size + state.queryParams.size, + state.selectedDataSourceId ); setDetectorsToDisplay(curDetectorsToDisplay); @@ -271,7 +293,11 @@ export const DetectorList = (props: ListProps) => { }, [confirmModalState.isRequestingToClose, isLoading]); const getUpdatedDetectors = async () => { - dispatch(getDetectorList(GET_ALL_DETECTORS_QUERY_PARAMS)); + dispatch( + getDetectorList( + getAllDetectorsQueryParamsWithDataSourceId(state.selectedDataSourceId) + ) + ); }; const handlePageChange = (pageNumber: number) => { @@ -558,6 +584,16 @@ export const DetectorList = (props: ListProps) => { }); }; + const handleDataSourceChange = (e) => { + const dataConnectionId = e[0] ? e[0].id : undefined; + + setState({ + ...state, + page: 0, + selectedDataSourceId: dataConnectionId, + }); + }; + const getConfirmModal = () => { if (confirmModalState.isOpen) { //@ts-ignore @@ -632,9 +668,28 @@ export const DetectorList = (props: ListProps) => { const confirmModal = getConfirmModal(); + const DataSourceMenu = + props.dataSourceManagement.ui.getDataSourceMenu(); + const renderDataSourceComponent = useMemo(() => { + return ( + + handleDataSourceChange(dataSources), + }} + /> + ); + }, [getSavedObjectsClient(), getNotifications(), props.setActionMenu]); + return ( + {props.dataSourceEnabled && renderDataSourceComponent} void; +} export function Main(props: MainProps) { + const { dataSourceEnabled, dataSourceManagement, setHeaderActionMenu } = + props; + const hideSideNavBar = useSelector( (state: AppState) => state.adApp.hideSideNavBar ); @@ -83,7 +91,12 @@ export function Main(props: MainProps) { exact path={APP_PATH.LIST_DETECTORS} render={(props: RouteComponentProps) => ( - + )} /> { + detectors.forEach((detector) => { + detector.dataSourceId = dataSourceId; + }); return detectors.slice(size * page, page * size + size); }; @@ -112,3 +116,15 @@ export const formatNumber = (data: any) => { return ''; } }; + +export const getAllDetectorsQueryParamsWithDataSourceId = ( + dataSourceId: string +) => ({ + from: 0, + search: '', + indices: '', + size: MAX_DETECTORS, + sortDirection: SORT_DIRECTION.ASC, + sortField: 'name', + dataSourceId: dataSourceId, +}); diff --git a/public/plugin.ts b/public/plugin.ts index 8a4dba30..cf048d46 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -44,6 +44,7 @@ import { } from '../../../src/plugins/vis_augmenter/public'; import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; import { DataPublicPluginStart } from '../../../src/plugins/data/public'; +import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public'; declare module '../../../src/plugins/ui_actions/public' { export interface ActionContextMapping { @@ -56,6 +57,7 @@ export interface AnomalyDetectionSetupDeps { embeddable: EmbeddableSetup; notifications: NotificationsSetup; visAugmenter: VisAugmenterSetup; + dataSourceManagement: DataSourceManagementPluginSetup; //uiActions: UiActionsSetup; } @@ -83,7 +85,12 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin mount: async (params: AppMountParameters) => { const { renderApp } = await import('./anomaly_detection_app'); const [coreStart] = await core.getStartServices(); - return renderApp(coreStart, params); + return renderApp( + coreStart, + params, + plugins.dataSourceManagement, + plugins.dataSource + ); }, }); diff --git a/public/services.ts b/public/services.ts index 2a4e8890..ec164c14 100644 --- a/public/services.ts +++ b/public/services.ts @@ -40,7 +40,7 @@ export const [getQueryService, setQueryService] = createGetterSetter('Query'); export const [getSavedObjectsClient, setSavedObjectsClient] = -createGetterSetter('SavedObjectsClient'); + createGetterSetter('SavedObjectsClient'); // This is primarily used for mocking this module and each of its fns in tests. export default { @@ -52,5 +52,5 @@ export default { getOverlays, setUISettings, setQueryService, - getSavedObjectsClient + getSavedObjectsClient, }; diff --git a/server/models/types.ts b/server/models/types.ts index 24fa7bb3..36f6443f 100644 --- a/server/models/types.ts +++ b/server/models/types.ts @@ -89,6 +89,7 @@ export type GetDetectorsQueryParams = { indices?: string; sortDirection: SORT_DIRECTION; sortField: string; + dataSourceId?: string; }; export type GetAdMonitorsQueryParams = { diff --git a/server/plugin.ts b/server/plugin.ts index 91d4461d..8c446e36 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -57,7 +57,10 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin this.logger = initializerContext.logger.get(); this.globalConfig$ = initializerContext.config.legacy.globalConfig$; } - public async setup(core: CoreSetup, {dataSource} : ADPluginSetupDependencies) { + public async setup( + core: CoreSetup, + { dataSource }: ADPluginSetupDependencies + ) { // Get any custom/overridden headers const globalConfig = await this.globalConfig$.pipe(first()).toPromise(); const { customHeaders, ...rest } = globalConfig.opensearch; @@ -67,30 +70,16 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin let client: ILegacyClusterClient | undefined = undefined; if (!dataSourceEnabled) { - client = core.opensearch.legacy.createClient( - 'anomaly_detection', - { - plugins: [adPlugin, alertingPlugin], - customHeaders: { ...customHeaders, ...DEFAULT_HEADERS }, - ...rest, - } - ) + client = core.opensearch.legacy.createClient('anomaly_detection', { + plugins: [adPlugin, alertingPlugin], + customHeaders: { ...customHeaders, ...DEFAULT_HEADERS }, + ...rest, + }); } else { - dataSource.registerCustomApiSchema(adPlugin) - dataSource.registerCustomApiSchema(alertingPlugin) - + dataSource.registerCustomApiSchema(adPlugin); + dataSource.registerCustomApiSchema(alertingPlugin); } - // // Create OpenSearch client w/ relevant plugins and headers - // const client: ILegacyClusterClient = core.opensearch.legacy.createClient( - // 'anomaly_detection', - // { - // plugins: [adPlugin, alertingPlugin], - // customHeaders: { ...customHeaders, ...DEFAULT_HEADERS }, - // ...rest, - // } - // ); - // Create router const apiRouter: Router = createRouter( core.http.createRouter(), @@ -98,7 +87,7 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin ); // Create services & register with OpenSearch client - const adService = new AdService(client); + const adService = new AdService(client, dataSourceEnabled); const alertingService = new AlertingService(client); const opensearchService = new OpenSearchService(client); const sampleDataService = new SampleDataService(client); diff --git a/server/routes/ad.ts b/server/routes/ad.ts index 5241c436..65487e7c 100644 --- a/server/routes/ad.ts +++ b/server/routes/ad.ts @@ -24,6 +24,7 @@ import { CUSTOM_AD_RESULT_INDEX_PREFIX, } from '../utils/constants'; import { + getClientBasedOnDataSource, mapKeysDeep, toCamel, toFixedNumberForAnomaly, @@ -103,9 +104,11 @@ export function registerADRoutes(apiRouter: Router, adService: AdService) { export default class AdService { private client: any; + dataSourceEnabled: boolean; - constructor(client: any) { + constructor(client: any, dataSourceEnabled: boolean) { this.client = client; + this.dataSourceEnabled = dataSourceEnabled; } deleteDetector = async ( @@ -592,6 +595,7 @@ export default class AdService { indices = '', sortDirection = SORT_DIRECTION.DESC, sortField = 'name', + dataSourceId = '', } = request.query as GetDetectorsQueryParams; const mustQueries = []; if (search.trim()) { @@ -634,10 +638,16 @@ export default class AdService { }, }, }; - const response: any = await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchDetector', { body: requestBody }); - + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + dataSourceId, + this.client + ); + const response = await callWithRequest('ad.searchDetector', { + body: requestBody, + }); const totalDetectors = get(response, 'hits.total.value', 0); //Get all detectors from search detector API @@ -668,9 +678,9 @@ export default class AdService { resultIndex: CUSTOM_AD_RESULT_INDEX_PREFIX + '*', onlyQueryCustomResultIndex: 'false', } as {}; - const aggregationResult = await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchResultsFromCustomResultIndex', { + const aggregationResult = await callWithRequest( + 'ad.searchResultsFromCustomResultIndex', + { ...requestParams, body: getResultAggregationQuery(allDetectorIds, { from, @@ -680,7 +690,8 @@ export default class AdService { search, indices, }), - }); + } + ); const aggsDetectors = get( aggregationResult, 'aggregations.unique_detectors.buckets', @@ -733,16 +744,12 @@ export default class AdService { let realtimeTasksResponse = {} as any; let historicalTasksResponse = {} as any; try { - realtimeTasksResponse = await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchTasks', { - body: getLatestDetectorTasksQuery(true), - }); - historicalTasksResponse = await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchTasks', { - body: getLatestDetectorTasksQuery(false), - }); + realtimeTasksResponse = await callWithRequest('ad.searchTasks', { + body: getLatestDetectorTasksQuery(true), + }); + historicalTasksResponse = await callWithRequest('ad.searchTasks', { + body: getLatestDetectorTasksQuery(false), + }); } catch (err) { if (!isIndexNotFoundError(err)) { throw err; diff --git a/server/utils/helpers.ts b/server/utils/helpers.ts index 15c80b3e..6233a007 100644 --- a/server/utils/helpers.ts +++ b/server/utils/helpers.ts @@ -20,6 +20,10 @@ import { } from 'lodash'; import { MIN_IN_MILLI_SECS } from './constants'; +import { + LegacyCallAPIOptions, + OpenSearchDashboardsRequest, +} from '../../../../src/core/server'; export const SHOW_DECIMAL_NUMBER_THRESHOLD = 0.01; @@ -81,3 +85,23 @@ export const prettifyErrorMessage = (rawErrorMessage: string) => { return `User ${match[2]} has no permissions to [${match[1]}].`; } }; + +export function getClientBasedOnDataSource( + context: any, + dataSourceEnabled: boolean, + request: OpenSearchDashboardsRequest, + dataSourceId: string, + client: any +): ( + endpoint: string, + clientParams?: Record, + options?: LegacyCallAPIOptions +) => any { + if (dataSourceEnabled && dataSourceId && dataSourceId.trim().length != 0) { + // client for remote cluster + return context.dataSource.opensearch.legacy.getClient(dataSourceId).callAPI; + } else { + // fall back to default local cluster + return client.asScoped(request).callAsCurrentUser; + } +} From aa5e25d8f48887f23119c7be02c2a199b3e938d1 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Fri, 5 Apr 2024 16:41:19 -0700 Subject: [PATCH 08/26] test Signed-off-by: Jackie Han --- opensearch_dashboards.json | 3 +- public/models/interfaces.ts | 1 + .../Dashboard/Container/DashboardOverview.tsx | 69 +++- public/pages/Dashboard/utils/constants.ts | 10 + .../components/Datasource/DataSource.tsx | 14 - .../containers/DetectorDetail.tsx | 38 ++- .../containers/AnomalyResults.tsx | 2 +- .../containers/AnomalyResultsLiveChart.tsx | 9 +- .../components/ListFilters/ListFilters.tsx | 16 +- .../DetectorsList/containers/List/List.tsx | 15 +- public/pages/DetectorsList/utils/helpers.ts | 3 +- .../pages/DetectorsList/utils/tableUtils.tsx | 4 +- .../SampleDataBox/SampleDataBox.tsx | 3 +- .../containers/AnomalyDetectionOverview.tsx | 66 +++- public/pages/main/Main.tsx | 80 +++-- public/pages/utils/constants.ts | 23 ++ public/pages/utils/helpers.ts | 14 +- public/redux/reducers/ad.ts | 35 ++- server/models/types.ts | 4 + server/plugin.ts | 6 +- server/routes/ad.ts | 297 +++++++++++++----- server/routes/alerting.ts | 31 +- server/routes/opensearch.ts | 89 ++++-- server/routes/sampleData.ts | 4 +- server/utils/helpers.ts | 5 +- 25 files changed, 627 insertions(+), 214 deletions(-) diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index 55581d80..d2e91e3c 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -5,8 +5,7 @@ "configPath": [ "anomaly_detection_dashboards" ], - "optionalPlugins": ["dataSource"], - "requiredBundles": ["dataSourceManagement"], + "optionalPlugins": ["dataSource","dataSourceManagement"], "requiredPlugins": [ "opensearchDashboardsUtils", "expressions", diff --git a/public/models/interfaces.ts b/public/models/interfaces.ts index ecce16ef..881e614a 100644 --- a/public/models/interfaces.ts +++ b/public/models/interfaces.ts @@ -205,6 +205,7 @@ export type Detector = { taskState?: DETECTOR_STATE; taskProgress?: number; taskError?: string; + dataSourceId? : string; }; export type DetectorListItem = { diff --git a/public/pages/Dashboard/Container/DashboardOverview.tsx b/public/pages/Dashboard/Container/DashboardOverview.tsx index 67b3d433..2bf95331 100644 --- a/public/pages/Dashboard/Container/DashboardOverview.tsx +++ b/public/pages/Dashboard/Container/DashboardOverview.tsx @@ -12,6 +12,7 @@ import React, { Fragment, useState, useEffect } from 'react'; import { AnomaliesLiveChart } from '../Components/AnomaliesLiveChart'; import { AnomaliesDistributionChart } from '../Components/AnomaliesDistribution'; +import queryString from 'querystring'; import { useDispatch, useSelector } from 'react-redux'; import { get, isEmpty, cloneDeep } from 'lodash'; @@ -29,17 +30,17 @@ import { } from '@elastic/eui'; import { AnomalousDetectorsList } from '../Components/AnomalousDetectorsList'; import { - GET_ALL_DETECTORS_QUERY_PARAMS, ALL_DETECTORS_MESSAGE, ALL_DETECTOR_STATES_MESSAGE, ALL_INDICES_MESSAGE, + getAllDetectorsQueryParamsWithDataSourceId } from '../utils/constants'; import { AppState } from '../../../redux/reducers'; -import { CatIndex, IndexAlias } from '../../../../server/models/types'; +import { CatIndex, IndexAlias, MDSQueryParams } from '../../../../server/models/types'; import { getVisibleOptions } from '../../utils/helpers'; import { BREADCRUMBS } from '../../../utils/constants'; import { DETECTOR_STATE } from '../../../../server/utils/constants'; -import { getDetectorStateOptions } from '../../DetectorsList/utils/helpers'; +import { getDetectorStateOptions, getURLQueryParams } from '../../DetectorsList/utils/helpers'; import { DashboardHeader } from '../Components/utils/DashboardHeader'; import { EmptyDashboard } from '../Components/EmptyDashboard/EmptyDashboard'; import { @@ -47,9 +48,26 @@ import { NO_PERMISSIONS_KEY_WORD, } from '../../../../server/utils/helpers'; import { CoreServicesContext } from '../../../components/CoreServices/CoreServices'; -import { CoreStart } from '../../../../../../src/core/public'; +import { CoreStart, MountPoint } from '../../../../../../src/core/public'; +import { DataSourceManagementPluginSetup, DataSourceSelectableConfig } from '../../../../../../src/plugins/data_source_management/public'; +import { getNotifications, getSavedObjectsClient } from '../../../services'; +import { RouteComponentProps } from 'react-router-dom'; -export function DashboardOverview() { +export interface DashboardOverviewRouterParams { + dataSourceId: string; +} + +interface OverviewProps extends RouteComponentProps { + dataSourceManagement: DataSourceManagementPluginSetup; + setActionMenu: (menuMount: MountPoint | undefined) => void; +} + +interface MDSOverviewState { + queryParams: MDSQueryParams; + selectedDataSourceId: string; +} + +export function DashboardOverview(props: OverviewProps) { const core = React.useContext(CoreServicesContext) as CoreStart; const dispatch = useDispatch(); const adState = useSelector((state: AppState) => state.ad); @@ -65,6 +83,12 @@ export function DashboardOverview() { const [selectedDetectorsName, setSelectedDetectorsName] = useState( [] as string[] ); + const queryParams = getURLQueryParams(props.location); + const [MDSOverviewState, setMDSOverviewState] = useState({ + queryParams, + selectedDataSourceId: queryParams.dataSourceId ? queryParams.dataSourceId : '', + }); + const getDetectorOptions = (detectorsIdMap: { [key: string]: DetectorListItem; }) => { @@ -108,6 +132,14 @@ export function DashboardOverview() { setAllDetectorStatesSelected(isEmpty(selectedStates)); }; + const handleDataSourceChange = (e) => { + const dataConnectionId = e[0] ? e[0].id : undefined; + setMDSOverviewState({ + queryParams: dataConnectionId, + selectedDataSourceId: dataConnectionId, + }); + } + const opensearchState = useSelector((state: AppState) => state.opensearch); const [selectedIndices, setSelectedIndices] = useState([] as string[]); @@ -157,14 +189,22 @@ export function DashboardOverview() { }; const intializeDetectors = async () => { - dispatch(getDetectorList(GET_ALL_DETECTORS_QUERY_PARAMS)); + dispatch(getDetectorList(getAllDetectorsQueryParamsWithDataSourceId(MDSOverviewState.selectedDataSourceId))); dispatch(getIndices('')); dispatch(getAliases('')); }; useEffect(() => { + const { history, location } = props; + const updatedParams = { + dataSourceId: MDSOverviewState.selectedDataSourceId, + }; + history.replace({ + ...location, + search: queryString.stringify(updatedParams), + }) intializeDetectors(); - }, []); + }, [MDSOverviewState]); useEffect(() => { if (errorGettingDetectors) { @@ -193,13 +233,26 @@ export function DashboardOverview() { filterSelectedDetectors( selectedDetectorsName, selectedDetectorStates, - selectedIndices + selectedIndices, ); }, [selectedDetectorsName, selectedIndices, selectedDetectorStates]); + const DataSourceMenu = props.dataSourceManagement.ui.getDataSourceMenu(); + return (
    + handleDataSourceChange(dataSources), + }} + /> 0} /> {isLoadingDetectors ? (
    diff --git a/public/pages/Dashboard/utils/constants.ts b/public/pages/Dashboard/utils/constants.ts index e673de09..bbc36680 100644 --- a/public/pages/Dashboard/utils/constants.ts +++ b/public/pages/Dashboard/utils/constants.ts @@ -81,6 +81,16 @@ export const GET_ALL_DETECTORS_QUERY_PARAMS = { sortField: 'name', }; +export const getAllDetectorsQueryParamsWithDataSourceId = (dataSourceId: string) => ({ + from: 0, + search: '', + indices: '', + size: MAX_DETECTORS, + sortDirection: SORT_DIRECTION.ASC, + sortField: 'name', + dataSourceId: dataSourceId, +}); + export const ALL_DETECTORS_MESSAGE = 'All detectors'; export const ALL_DETECTOR_STATES_MESSAGE = 'All detector states'; export const ALL_INDICES_MESSAGE = 'All indices'; diff --git a/public/pages/DefineDetector/components/Datasource/DataSource.tsx b/public/pages/DefineDetector/components/Datasource/DataSource.tsx index 7863a305..a65c6e8e 100644 --- a/public/pages/DefineDetector/components/Datasource/DataSource.tsx +++ b/public/pages/DefineDetector/components/Datasource/DataSource.tsx @@ -33,8 +33,6 @@ import { ModelConfigurationFormikValues } from '../../../ConfigureModel/models/i import { INITIAL_MODEL_CONFIGURATION_VALUES } from '../../../ConfigureModel/utils/constants'; import { FILTER_TYPES } from '../../../../models/interfaces'; import { getNotifications, getSavedObjectsClient } from '../../../../services'; -import { ClusterSelector } from '../../../../../../../src/plugins/data_source_management/public'; - interface DataSourceProps { formikProps: FormikProps; @@ -105,18 +103,6 @@ export function DataSource(props: DataSourceProps) { return ( - - - - {props.isEdit && isDifferentIndex() ? (
    diff --git a/public/pages/DetectorDetail/containers/DetectorDetail.tsx b/public/pages/DetectorDetail/containers/DetectorDetail.tsx index 20f7344f..692f8483 100644 --- a/public/pages/DetectorDetail/containers/DetectorDetail.tsx +++ b/public/pages/DetectorDetail/containers/DetectorDetail.tsx @@ -24,10 +24,10 @@ import { EuiLoadingSpinner, EuiButton, } from '@elastic/eui'; -import { CoreStart } from '../../../../../../src/core/public'; +import { CoreStart, MountPoint } from '../../../../../../src/core/public'; import { CoreServicesContext } from '../../../components/CoreServices/CoreServices'; import { get, isEmpty } from 'lodash'; -import { RouteComponentProps, Switch, Route, Redirect } from 'react-router-dom'; +import { RouteComponentProps, Switch, Route, Redirect, useLocation } from 'react-router-dom'; import { useDispatch, useSelector } from 'react-redux'; import { useFetchDetectorInfo } from '../../CreateDetectorSteps/hooks/useFetchDetectorInfo'; import { useHideSideNavBar } from '../../main/hooks/useHideSideNavBar'; @@ -58,12 +58,17 @@ import { import { DETECTOR_STATE } from '../../../../server/utils/constants'; import { CatIndex } from '../../../../server/models/types'; import { containsIndex } from '../utils/helpers'; +import { DataSourceManagementPluginSetup, DataSourceViewConfig } from '../../../../../../src/plugins/data_source_management/public'; export interface DetectorRouterProps { detectorId?: string; } interface DetectorDetailProps - extends RouteComponentProps {} + extends RouteComponentProps { + dataSourceEnabled: boolean; + dataSourceManagement: DataSourceManagementPluginSetup; + setActionMenu: (menuMount: MountPoint | undefined) => void; + } const tabs = [ { @@ -100,9 +105,14 @@ interface DetectorDetailModel { } export const DetectorDetail = (props: DetectorDetailProps) => { + console.log(props); const core = React.useContext(CoreServicesContext) as CoreStart; const dispatch = useDispatch(); const detectorId = get(props, 'match.params.detectorId', '') as string; + const location = useLocation(); + const queryParams = new URLSearchParams(location.search); + const dataSourceId = queryParams.get('dataSourceId') as string; + const { detector, hasError, isLoadingDetector, errorMessage } = useFetchDetectorInfo(detectorId); const { monitor, fetchMonitorError, isLoadingMonitor } = @@ -201,7 +211,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { ...detectorDetailModel, selectedTab: DETECTOR_DETAIL_TABS.CONFIGURATIONS, }); - props.history.push(`/detectors/${detectorId}/configurations`); + props.history.push(`/detectors/${detectorId}/configurations?dataSourceId=${dataSourceId}`); }, []); const handleSwitchToHistoricalTab = useCallback(() => { @@ -209,7 +219,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { ...detectorDetailModel, selectedTab: DETECTOR_DETAIL_TABS.HISTORICAL, }); - props.history.push(`/detectors/${detectorId}/historical`); + props.history.push(`/detectors/${detectorId}/historical?dataSourceId=${dataSourceId}`); }, []); const handleTabChange = (route: DETECTOR_DETAIL_TABS) => { @@ -217,7 +227,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { ...detectorDetailModel, selectedTab: route, }); - props.history.push(`/detectors/${detectorId}/${route}`); + props.history.push(`/detectors/${detectorId}/${route}?dataSourceId=${dataSourceId}`); }; const hideMonitorCalloutModal = () => { @@ -245,9 +255,9 @@ export const DetectorDetail = (props: DetectorDetailProps) => { const listener: Listener = { onSuccess: () => { if (detectorDetailModel.showStopDetectorModalFor === 'detector') { - props.history.push(`/detectors/${detectorId}/edit`); + props.history.push(`/detectors/${detectorId}/edit?dataSourceId=${dataSourceId}`); } else { - props.history.push(`/detectors/${detectorId}/features`); + props.history.push(`/detectors/${detectorId}/features?dataSourceId=${dataSourceId}`); } hideStopDetectorModal(); }, @@ -350,6 +360,9 @@ export const DetectorDetail = (props: DetectorDetailProps) => { > ) : null; + + const DataSourceMenu = props.dataSourceManagement.ui.getDataSourceMenu(); + return ( {!isEmpty(detector) && !hasError ? ( @@ -361,6 +374,14 @@ export const DetectorDetail = (props: DetectorDetailProps) => { : { ...lightStyles, flexGrow: 'unset' }), }} > + { handleStartAdJob(detectorId)} onStopDetector={() => handleStopAdJob(detectorId)} onSwitchToConfiguration={handleSwitchToConfigurationTab} diff --git a/public/pages/DetectorResults/containers/AnomalyResults.tsx b/public/pages/DetectorResults/containers/AnomalyResults.tsx index 827570ce..a08fe705 100644 --- a/public/pages/DetectorResults/containers/AnomalyResults.tsx +++ b/public/pages/DetectorResults/containers/AnomalyResults.tsx @@ -575,4 +575,4 @@ export function AnomalyResults(props: AnomalyResultsProps) { ); -} +} \ No newline at end of file diff --git a/public/pages/DetectorResults/containers/AnomalyResultsLiveChart.tsx b/public/pages/DetectorResults/containers/AnomalyResultsLiveChart.tsx index 7ba1d2fb..3ccee6e2 100644 --- a/public/pages/DetectorResults/containers/AnomalyResultsLiveChart.tsx +++ b/public/pages/DetectorResults/containers/AnomalyResultsLiveChart.tsx @@ -130,7 +130,7 @@ export const AnomalyResultsLiveChart = ( detectionInterval, intervals ); - await dispatch( + await dispatch( getDetectorLiveResults( detectorId, queryParams, @@ -139,6 +139,13 @@ export const AnomalyResultsLiveChart = ( true ) ); + console.log(getDetectorLiveResults( + detectorId, + queryParams, + false, + resultIndex, + true + )); } catch (err) { console.error( `Failed to get live anomaly result for detector ${detectorId}`, diff --git a/public/pages/DetectorsList/components/ListFilters/ListFilters.tsx b/public/pages/DetectorsList/components/ListFilters/ListFilters.tsx index 8461210e..52fc1be9 100644 --- a/public/pages/DetectorsList/components/ListFilters/ListFilters.tsx +++ b/public/pages/DetectorsList/components/ListFilters/ListFilters.tsx @@ -17,11 +17,9 @@ import { EuiFlexItem, EuiPagination, } from '@elastic/eui'; -import React, { useState } from 'react'; +import React from 'react'; import { getDetectorStateOptions } from '../../utils/helpers'; import { DETECTOR_STATE } from '../../../../utils/constants'; -import { ClusterSelector } from '../../../../../../../src/plugins/data_source_management/public'; -import { getNotifications, getSavedObjectsClient } from '../../../../services'; interface ListFiltersProps { activePage: number; @@ -32,7 +30,6 @@ interface ListFiltersProps { indexOptions: EuiComboBoxOptionProps[]; onDetectorStateChange: (options: EuiComboBoxOptionProps[]) => void; onIndexChange: (options: EuiComboBoxOptionProps[]) => void; - onDataSourceChange: (e: any) => void; onSearchDetectorChange: (e: React.ChangeEvent) => void; onSearchIndexChange: (searchValue: string) => void; onPageClick: (pageNumber: number) => void; @@ -66,17 +63,6 @@ export const ListFilters = (props: ListFiltersProps) => ( fullWidth={true} /> - - - - { const visibleAliases = get(opensearchState, 'aliases', []) as IndexAlias[]; const indexOptions = getVisibleOptions(visibleIndices, visibleAliases); + const queryParams = getURLQueryParams(props.location); const [state, setState] = useState({ page: 0, - queryParams: getURLQueryParams(props.location), + queryParams: queryParams, selectedDetectorStates: ALL_DETECTOR_STATES, selectedIndices: queryParams.indices ? queryParams.indices.split(',') @@ -258,7 +259,7 @@ export const DetectorList = (props: ListProps) => { state.selectedIndices, state.selectedDetectorStates, state.queryParams.sortField, - state.queryParams.sortDirection + state.queryParams.sortDirection, ); setSelectedDetectors(curSelectedDetectors); @@ -291,7 +292,6 @@ export const DetectorList = (props: ListProps) => { } } }, [confirmModalState.isRequestingToClose, isLoading]); - const getUpdatedDetectors = async () => { dispatch( getDetectorList( @@ -378,14 +378,6 @@ export const DetectorList = (props: ListProps) => { }); }; - const [selectedDataSource, setSelectedDataSource] = useState(); - - const handleDataSourceChange = (e) => { - const dataConnectionId = e[0] ? e[0].id : undefined; - setSelectedDataSource(dataConnectionId); - console.log(dataConnectionId); - } - const handleResetFilter = () => { setState((state) => ({ ...state, @@ -731,7 +723,6 @@ export const DetectorList = (props: ListProps) => { indexOptions={indexOptions} onDetectorStateChange={handleDetectorStateChange} onIndexChange={handleIndexChange} - onDataSourceChange={handleDataSourceChange} onSearchDetectorChange={handleSearchDetectorChange} onSearchIndexChange={handleSearchIndexChange} onPageClick={handlePageChange} diff --git a/public/pages/DetectorsList/utils/helpers.ts b/public/pages/DetectorsList/utils/helpers.ts index 89d9075c..e805e460 100644 --- a/public/pages/DetectorsList/utils/helpers.ts +++ b/public/pages/DetectorsList/utils/helpers.ts @@ -21,7 +21,7 @@ import { DETECTOR_ACTION } from '../utils/constants'; export const getURLQueryParams = (location: { search: string; }): GetDetectorsQueryParams => { - const { from, size, search, indices, sortField, sortDirection } = + const { from, size, search, indices, sortField, sortDirection, dataSourceId } = queryString.parse(location.search) as { [key: string]: string }; return { // @ts-ignore @@ -40,6 +40,7 @@ export const getURLQueryParams = (location: { typeof sortDirection !== 'string' ? DEFAULT_QUERY_PARAMS.sortDirection : (sortDirection as SORT_DIRECTION), + dataSourceId: typeof dataSourceId !== 'string' ? '' : dataSourceId, }; }; diff --git a/public/pages/DetectorsList/utils/tableUtils.tsx b/public/pages/DetectorsList/utils/tableUtils.tsx index b1e4b8bd..8c763832 100644 --- a/public/pages/DetectorsList/utils/tableUtils.tsx +++ b/public/pages/DetectorsList/utils/tableUtils.tsx @@ -65,7 +65,7 @@ export const staticColumn = [ align: 'left', width: '15%', render: (name: string, detector: Detector) => ( - + {name} ), @@ -112,7 +112,7 @@ export const staticColumn = [ width: '15%', render: (name: string, detector: Detector) => { return !isEmpty(detector.taskId) ? ( - + View results ) : ( diff --git a/public/pages/Overview/components/SampleDataBox/SampleDataBox.tsx b/public/pages/Overview/components/SampleDataBox/SampleDataBox.tsx index 0f544d27..c968036f 100644 --- a/public/pages/Overview/components/SampleDataBox/SampleDataBox.tsx +++ b/public/pages/Overview/components/SampleDataBox/SampleDataBox.tsx @@ -34,6 +34,7 @@ interface SampleDataBoxProps { isDataLoaded: boolean; detectorId: string; buttonDataTestSubj?: string; + dataSourceId?: string; } export const SampleDataBox = (props: SampleDataBoxProps) => { @@ -114,7 +115,7 @@ export const SampleDataBox = (props: SampleDataBoxProps) => { {props.isDataLoaded ? ( View detector and sample data diff --git a/public/pages/Overview/containers/AnomalyDetectionOverview.tsx b/public/pages/Overview/containers/AnomalyDetectionOverview.tsx index 5d84779e..80e40dd5 100644 --- a/public/pages/Overview/containers/AnomalyDetectionOverview.tsx +++ b/public/pages/Overview/containers/AnomalyDetectionOverview.tsx @@ -34,6 +34,7 @@ import { SAMPLE_TYPE } from '../../../../server/utils/constants'; import { GET_SAMPLE_DETECTORS_QUERY_PARAMS, GET_SAMPLE_INDICES_QUERY, + getSampleDetectorsQueryParamsWithDataSouceId, } from '../../utils/constants'; import { AppState } from '../../../redux/reducers'; import { getDetectorList } from '../../../redux/reducers/ad'; @@ -54,14 +55,31 @@ import { import { SampleDataBox } from '../components/SampleDataBox/SampleDataBox'; import { SampleDetailsFlyout } from '../components/SampleDetailsFlyout/SampleDetailsFlyout'; import { prettifyErrorMessage } from '../../../../server/utils/helpers'; -import { CoreStart } from '../../../../../../src/core/public'; +import { CoreStart, MountPoint } from '../../../../../../src/core/public'; import { CoreServicesContext } from '../../../components/CoreServices/CoreServices'; import ContentPanel from '../../../components/ContentPanel/ContentPanel'; import { CreateWorkflowStepDetails } from '../components/CreateWorkflowStepDetails'; import { CreateWorkflowStepSeparator } from '../components/CreateWorkflowStepSeparator'; +import { DataSourceManagementPluginSetup, DataSourceSelectableConfig } from '../../../../../../src/plugins/data_source_management/public'; +import { getNotifications, getSavedObjectsClient } from '../../../../public/services'; +import { MDSQueryParams } from 'server/models/types'; +import { RouteComponentProps } from 'react-router-dom'; +import queryString from 'querystring'; +import { getURLQueryParams } from '../../../../public/pages/DetectorsList/utils/helpers'; -interface AnomalyDetectionOverviewProps { +interface AnomalyDetectionOverviewProps extends RouteComponentProps { isLoadingDetectors: boolean; + dataSourceManagement: DataSourceManagementPluginSetup; + setActionMenu: (menuMount: MountPoint | undefined) => void; +} + +interface OverviewRouterParams { + dataSourceId: string; +} + +interface MDSOverviewState { + queryParams: MDSQueryParams; + selectedDataSourceId: string; } export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { @@ -71,6 +89,8 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { (state: AppState) => state.opensearch.indices ); + console.log(props); + const allSampleDetectors = Object.values( useSelector((state: AppState) => state.ad.detectorList) ); @@ -86,9 +106,15 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { useState(false); const [showHostHealthDetailsFlyout, setShowHostHealthDetailsFlyout] = useState(false); + + const queryParams = getURLQueryParams(props.location); + const [MDSOverviewState, setMDSOverviewState] = useState({ + queryParams, + selectedDataSourceId: queryParams.dataSourceId ? queryParams.dataSourceId : '', + }); const getAllSampleDetectors = async () => { - await dispatch(getDetectorList(GET_SAMPLE_DETECTORS_QUERY_PARAMS)).catch( + await dispatch(getDetectorList(getSampleDetectorsQueryParamsWithDataSouceId(MDSOverviewState.selectedDataSourceId))).catch( (error: any) => { console.error('Error getting all detectors: ', error); } @@ -108,9 +134,17 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { // Getting all initial sample detectors & indices useEffect(() => { + const { history, location } = props; + const updatedParams = { + dataSourceId: MDSOverviewState.selectedDataSourceId, + }; + history.replace({ + ...location, + search: queryString.stringify(updatedParams), + }) getAllSampleDetectors(); getAllSampleIndices(); - }, []); + }, [MDSOverviewState]); // Create and populate sample index, create and start sample detector const handleLoadData = async ( @@ -175,6 +209,16 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { } }; + const handleDataSourceChange = (e) => { + const dataConnectionId = e[0] ? e[0].id : undefined; + setMDSOverviewState({ + queryParams: dataConnectionId, + selectedDataSourceId: dataConnectionId, + }); + } + + const DataSourceMenu = props.dataSourceManagement.ui.getDataSourceMenu(); + return props.isLoadingDetectors ? (
    @@ -182,6 +226,17 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { ) : ( + handleDataSourceChange(dataSources), + }} + /> @@ -274,6 +329,7 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { sampleHttpResponses.detectorName, sampleHttpResponses.legacyDetectorName )} + dataSourceId={MDSOverviewState.selectedDataSourceId} /> @@ -306,6 +362,7 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { sampleEcommerce.detectorName, sampleEcommerce.legacyDetectorName )} + dataSourceId={MDSOverviewState.selectedDataSourceId} /> @@ -340,6 +397,7 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { sampleHostHealth.detectorName, sampleHostHealth.legacyDetectorName )} + dataSourceId={MDSOverviewState.selectedDataSourceId} /> diff --git a/public/pages/main/Main.tsx b/public/pages/main/Main.tsx index 9c2b209d..38eae723 100644 --- a/public/pages/main/Main.tsx +++ b/public/pages/main/Main.tsx @@ -21,11 +21,12 @@ import { APP_PATH } from '../../utils/constants'; import { DetectorDetail } from '../DetectorDetail'; import { DefineDetector } from '../DefineDetector/containers/DefineDetector'; import { ConfigureModel } from '../ConfigureModel/containers/ConfigureModel'; -import { DashboardOverview } from '../Dashboard/Container/DashboardOverview'; +import { DashboardOverview, DashboardOverviewRouterParams } from '../Dashboard/Container/DashboardOverview'; import { CoreServicesConsumer } from '../../components/CoreServices/CoreServices'; import { CoreStart, MountPoint } from '../../../../../src/core/public'; import { AnomalyDetectionOverview } from '../Overview'; import { DataSourceManagementPluginSetup } from '../../../../../src/plugins/data_source_management/public'; +import { getURLQueryParams } from '../DetectorsList/utils/helpers'; enum Navigation { AnomalyDetection = 'Anomaly detection', @@ -51,6 +52,23 @@ export function Main(props: MainProps) { const totalDetectors = adState.totalDetectors; const errorGettingDetectors = adState.errorMessage; const isLoadingDetectors = adState.requesting; + const queryParams = getURLQueryParams(props.location); + const dataSourceId = queryParams.dataSourceId ? queryParams.dataSourceId : ''; + + const constructHrefWithDataSourceId = (basePath, existingParams, dataSourceId) => { + // Construct the full URL + const fullUrl = `${window.location.origin}${basePath}?${existingParams}`; + //console.log("fullUrl: ", fullUrl); + const url = new URL(fullUrl); + //console.log(url); + url.searchParams.set('dataSourceId', dataSourceId); // Append or update dataSourceId + + // Return the constructed URL, excluding the origin part to match your structure + return `#${url.pathname}${url.search}`; + }; + + const existingParams = "from=0&size=20&search=&indices=&sortField=name&sortDirection=asc&dataSourceId=4585f560-d1ef-11ee-aa63-2181676cc573"; + const sideNav = [ { name: Navigation.AnomalyDetection, @@ -60,13 +78,13 @@ export function Main(props: MainProps) { { name: Navigation.Dashboard, id: 1, - href: `#${APP_PATH.DASHBOARD}`, + href: `#${APP_PATH.DASHBOARD}?dataSourceId=${dataSourceId}`, isSelected: props.location.pathname === APP_PATH.DASHBOARD, }, { name: Navigation.Detectors, id: 2, - href: `#${APP_PATH.LIST_DETECTORS}`, + href: constructHrefWithDataSourceId(APP_PATH.LIST_DETECTORS, existingParams, dataSourceId), isSelected: props.location.pathname === APP_PATH.LIST_DETECTORS, }, ], @@ -85,7 +103,12 @@ export function Main(props: MainProps) { } + render={(props: RouteComponentProps) => + } /> ( - - )} + render={(props: RouteComponentProps) => { + return ( + + ); + }} /> ( + render={(props: RouteComponentProps) => ( )} /> - - {totalDetectors > 0 ? ( - //
    - - ) : ( - - )} - + ) => + totalDetectors > 0 ? ( + + ) : ( + + ) + } + /> + + + diff --git a/public/pages/utils/constants.ts b/public/pages/utils/constants.ts index e6f9fc0c..335d9b1c 100644 --- a/public/pages/utils/constants.ts +++ b/public/pages/utils/constants.ts @@ -53,6 +53,7 @@ export const DEFAULT_QUERY_PARAMS = { size: 20, sortDirection: SORT_DIRECTION.ASC, sortField: 'name', + dataSourceId: '', }; export const GET_ALL_DETECTORS_QUERY_PARAMS = { @@ -64,6 +65,28 @@ export const GET_ALL_DETECTORS_QUERY_PARAMS = { sortField: 'name', }; +export const getAllDetectorsQueryParamsWithDataSourceId = (dataSourceId: string) => ({ + from: 0, + search: '', + indices: '', + size: MAX_DETECTORS, + sortDirection: SORT_DIRECTION.ASC, + sortField: 'name', + dataSourceId: dataSourceId, +}); + + +export const getSampleDetectorsQueryParamsWithDataSouceId = (dataSourceId: string) => ( + { + from: 0, + search: 'sample', + indices: '', + size: MAX_DETECTORS, + sortDirection: SORT_DIRECTION.ASC, + sortField: 'name', + dataSourceId: dataSourceId, +}); + export const GET_SAMPLE_DETECTORS_QUERY_PARAMS = { from: 0, search: 'sample', diff --git a/public/pages/utils/helpers.ts b/public/pages/utils/helpers.ts index e57a957b..879b843c 100644 --- a/public/pages/utils/helpers.ts +++ b/public/pages/utils/helpers.ts @@ -9,7 +9,7 @@ * GitHub history for details. */ -import { CatIndex, IndexAlias } from '../../../server/models/types'; +import { CatIndex, IndexAlias, MDSQueryParams } from '../../../server/models/types'; import sortBy from 'lodash/sortBy'; import { DetectorListItem } from '../../models/interfaces'; import { SORT_DIRECTION } from '../../../server/utils/constants'; @@ -68,7 +68,7 @@ export const filterAndSortDetectors = ( selectedIndices: string[], selectedDetectorStates: DETECTOR_STATE[], sortField: string, - sortDirection: string + sortDirection: string, ) => { let filteredBySearch = search == '' @@ -128,3 +128,13 @@ export const getAllDetectorsQueryParamsWithDataSourceId = ( sortField: 'name', dataSourceId: dataSourceId, }); + +export const getMDSQueryParams = (location: { + search: string +}): MDSQueryParams => { + const params = new URLSearchParams(location.search); + const dataSourceId = params.get('dataSourceId'); + return { + dataSourceId: dataSourceId || '', + }; + } diff --git a/public/redux/reducers/ad.ts b/public/redux/reducers/ad.ts index d4d12ae6..6c92f415 100644 --- a/public/redux/reducers/ad.ts +++ b/public/redux/reducers/ad.ts @@ -18,7 +18,7 @@ import { import handleActions from '../utils/handleActions'; import { Detector, DetectorListItem } from '../../models/interfaces'; import { AD_NODE_API } from '../../../utils/constants'; -import { GetDetectorsQueryParams } from '../../../server/models/types'; +import { GetDetectorsQueryParams, MDSQueryParams } from '../../../server/models/types'; import { cloneDeep, get } from 'lodash'; import moment from 'moment'; import { DETECTOR_STATE } from '../../../server/utils/constants'; @@ -369,7 +369,8 @@ const reducer = handleActions( initialDetectorsState ); -export const createDetector = (requestBody: Detector): APIAction => ({ +export const createDetector = (requestBody: Detector): APIAction => ( + { type: CREATE_DETECTOR, request: (client: HttpSetup) => client.post(`..${AD_NODE_API.DETECTOR}`, { @@ -388,20 +389,24 @@ export const validateDetector = ( }), }); -export const getDetector = (detectorId: string): APIAction => ({ - type: GET_DETECTOR, - request: (client: HttpSetup) => - client.get(`..${AD_NODE_API.DETECTOR}/${detectorId}`), - detectorId, -}); +export const getDetector = (detectorId: string): APIAction => { + return { + type: GET_DETECTOR, + request: (client: HttpSetup) => + client.get(`..${AD_NODE_API.DETECTOR}/${detectorId}`), + detectorId, + }; +}; -export const getDetectorList = ( - queryParams: GetDetectorsQueryParams -): APIAction => ({ - type: GET_DETECTOR_LIST, - request: (client: HttpSetup) => - client.get(`..${AD_NODE_API.DETECTOR}`, { query: queryParams }), -}); + +export const getDetectorList = (queryParams: GetDetectorsQueryParams): APIAction => { + console.log(queryParams); + return { + type: GET_DETECTOR_LIST, + request: (client: HttpSetup) => + client.get(`..${AD_NODE_API.DETECTOR}`, { query: queryParams }), + }; +}; export const searchDetector = (requestBody: any): APIAction => ({ type: SEARCH_DETECTOR, diff --git a/server/models/types.ts b/server/models/types.ts index 36f6443f..f4bbaa59 100644 --- a/server/models/types.ts +++ b/server/models/types.ts @@ -109,6 +109,10 @@ export type DetectorResultsQueryParams = { dateRangeFilter?: DateRangeFilter; }; +export type MDSQueryParams = { + dataSourceId?: string; +} + export type Entity = { name: string; value: string; diff --git a/server/plugin.ts b/server/plugin.ts index 8c446e36..bffec10e 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -88,9 +88,9 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin // Create services & register with OpenSearch client const adService = new AdService(client, dataSourceEnabled); - const alertingService = new AlertingService(client); - const opensearchService = new OpenSearchService(client); - const sampleDataService = new SampleDataService(client); + const alertingService = new AlertingService(client, dataSourceEnabled); + const opensearchService = new OpenSearchService(client, dataSourceEnabled); + const sampleDataService = new SampleDataService(client, dataSourceEnabled); // Register server routes with the service registerADRoutes(apiRouter, adService); diff --git a/server/routes/ad.ts b/server/routes/ad.ts index 65487e7c..8016c443 100644 --- a/server/routes/ad.ts +++ b/server/routes/ad.ts @@ -16,6 +16,7 @@ import { Detector, GetDetectorsQueryParams, FeatureResult, + MDSQueryParams, } from '../models/types'; import { Router } from '../router'; import { @@ -118,11 +119,24 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; - const response = await this.client - .asScoped(request) - .callAsCurrentUser('ad.deleteDetector', { + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const response = await callWithRequest( + 'ad.deleteDetector', { detectorId, }); + + + // const response = await this.client + // .asScoped(request) + // .callAsCurrentUser('ad.deleteDetector', { + // detectorId, + // }); return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -149,11 +163,24 @@ export default class AdService { const requestBody = JSON.stringify( convertPreviewInputKeysToSnakeCase(request.body) ); - const response = await this.client - .asScoped(request) - .callAsCurrentUser('ad.previewDetector', { + + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const response = await callWithRequest( + 'ad.previewDetector', { body: requestBody, }); + + // const response = await this.client + // .asScoped(request) + // .callAsCurrentUser('ad.previewDetector', { + // body: requestBody, + // }); const transformedKeys = mapKeysDeep(response, toCamel); return opensearchDashboardsResponse.ok({ body: { @@ -180,6 +207,7 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; + const { dataSourceId } = request.params as { dataSourceId: string }; //@ts-ignore const ifSeqNo = request.body.seqNo; //@ts-ignore @@ -196,17 +224,26 @@ export default class AdService { }; let response; + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + dataSourceId, + this.client); + if (isNumber(ifSeqNo) && isNumber(ifPrimaryTerm)) { - response = await this.client - .asScoped(request) - .callAsCurrentUser('ad.updateDetector', params); - } else { - const client = context.dataSource.opensearch.legacy.getClient('4585f560-d1ef-11ee-aa63-2181676cc573'); + response = await callWithRequest( + 'ad.updateDetector', params); - response = await client - .callAPI('ad.createDetector', { - body: params.body, + // response = await this.client + // .asScoped(request) + // .callAsCurrentUser('ad.updateDetector', params); + } else { + response = await callWithRequest( + 'ad.createDetector', { + body: requestBody, }); + // response = await this.client // .asScoped(request) // .callAsCurrentUser('ad.createDetector', { @@ -248,13 +285,19 @@ export default class AdService { const requestBody = JSON.stringify( convertPreviewInputKeysToSnakeCase(request.body) ); - const client = context.dataSource.opensearch.legacy.getClient('4585f560-d1ef-11ee-aa63-2181676cc573'); + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); - const response = await this.client - .callAPI('ad.validateDetector', { + const response = await callWithRequest( + 'ad.validateDetector', { body: requestBody, validationType: validationType, }); + // const response = await this.client // .asScoped(request) // .callAsCurrentUser('ad.validateDetector', { @@ -285,10 +328,18 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; - const detectorResponse = await this.client - .asScoped(request) - .callAsCurrentUser('ad.getDetector', { - detectorId, + const { dataSourceId = '' } = request.query as MDSQueryParams; + console.log("getDetector: " + dataSourceId); + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const detectorResponse = await callWithRequest( + 'ad.getDetector', { + detectorId, }); // Populating static detector fields @@ -306,16 +357,22 @@ export default class AdService { let realtimeTasksResponse = {} as any; let historicalTasksResponse = {} as any; try { - realtimeTasksResponse = await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchTasks', { - body: getLatestTaskForDetectorQuery(detectorId, true), + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + "4585f560-d1ef-11ee-aa63-2181676cc573", + this.client); + + realtimeTasksResponse = await callWithRequest( + 'ad.searchTasks', { + body: getLatestTaskForDetectorQuery(detectorId, true), }); - historicalTasksResponse = await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchTasks', { + + historicalTasksResponse = await callWithRequest('ad.searchTasks', { body: getLatestTaskForDetectorQuery(detectorId, false), }); + } catch (err) { if (!isIndexNotFoundError(err)) { throw err; @@ -398,9 +455,18 @@ export default class AdService { requestPath = 'ad.startHistoricalDetector'; } - const response = await this.client - .asScoped(request) - .callAsCurrentUser(requestPath, requestParams); + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const response = await callWithRequest(requestPath, requestParams); + + // const response = await this.client + // .asScoped(request) + // .callAsCurrentUser(requestPath, requestParams); return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -424,20 +490,28 @@ export default class AdService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { try { - let { detectorId, isHistorical } = request.params as { + let { detectorId, isHistorical, dataSourceId } = request.params as { detectorId: string; isHistorical: any; + dataSourceId: string; }; isHistorical = JSON.parse(isHistorical) as boolean; const requestPath = isHistorical ? 'ad.stopHistoricalDetector' : 'ad.stopDetector'; - const response = await this.client - .asScoped(request) - .callAsCurrentUser(requestPath, { + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + dataSourceId, + this.client); + + const response = await callWithRequest( + requestPath, { detectorId, }); + return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -462,11 +536,25 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; - const response = await this.client - .asScoped(request) - .callAsCurrentUser('ad.detectorProfile', { + + + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const response = await callWithRequest( + 'ad.detectorProfile', { detectorId, }); + + // const response = await this.client + // .asScoped(request) + // .callAsCurrentUser('ad.detectorProfile', { + // detectorId, + // }); return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -491,9 +579,20 @@ export default class AdService { ): Promise> => { try { const requestBody = JSON.stringify(request.body); - const response: SearchResponse = await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchDetector', { body: requestBody }); + + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const response: SearchResponse = await callWithRequest( + 'ad.searchDetector', { body: requestBody }); + + // const response: SearchResponse = await this.client + // .asScoped(request) + // .callAsCurrentUser('ad.searchDetector', { body: requestBody }); const totalDetectors = get(response, 'hits.total.value', 0); const detectors = get(response, 'hits.hits', []).map((detector: any) => ({ ...convertDetectorKeysToCamelCase(detector._source), @@ -548,18 +647,37 @@ export default class AdService { onlyQueryCustomResultIndex: onlyQueryCustomResultIndex, } as {}; const requestBody = JSON.stringify(request.body); + + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + const response = !resultIndex - ? await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchResults', { - body: requestBody, - }) - : await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchResultsFromCustomResultIndex', { - ...requestParams, - body: requestBody, - }); + ? await callWithRequest( + 'ad.searchResults', { + body: requestBody, + }) + : await callWithRequest( + 'ad.searchResultsFromCustomResultIndex', { + ...requestParams, + body: requestBody, + }); + + // const response = !resultIndex + // ? await this.client + // .asScoped(request) + // .callAsCurrentUser('ad.searchResults', { + // body: requestBody, + // }) + // : await this.client + // .asScoped(request) + // .callAsCurrentUser('ad.searchResultsFromCustomResultIndex', { + // ...requestParams, + // body: requestBody, + // }); return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -829,13 +947,15 @@ export default class AdService { request: OpenSearchDashboardsRequest, opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { - let { id, isHistorical, resultIndex, onlyQueryCustomResultIndex } = + let { id, dataSourceId, isHistorical, resultIndex, onlyQueryCustomResultIndex } = request.params as { id: string; + dataSourceId: string; isHistorical: any; resultIndex: string; onlyQueryCustomResultIndex: boolean; }; + console.log("getAnomalyResults: " + dataSourceId); if ( !resultIndex || !resultIndex.startsWith(CUSTOM_AD_RESULT_INDEX_PREFIX) @@ -966,15 +1086,21 @@ export default class AdService { resultIndex: resultIndex, onlyQueryCustomResultIndex: onlyQueryCustomResultIndex, } as {}; + + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + dataSourceId, + this.client); + const response = !resultIndex - ? await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchResults', { + ? await callWithRequest( + 'ad.searchResults', { body: requestBody, }) - : await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchResultsFromCustomResultIndex', { + : await callWithRequest( + 'ad.searchResultsFromCustomResultIndex', { ...requestParams, body: requestBody, }); @@ -1071,13 +1197,27 @@ export default class AdService { ? 'ad.topHistoricalAnomalyResults' : 'ad.topAnomalyResults'; - const response = await this.client - .asScoped(request) - .callAsCurrentUser(requestPath, { + + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const response = await callWithRequest( + requestPath, { detectorId: detectorId, body: request.body, }); + // const response = await this.client + // .asScoped(request) + // .callAsCurrentUser(requestPath, { + // detectorId: detectorId, + // body: request.body, + // }); + return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -1100,15 +1240,21 @@ export default class AdService { request: OpenSearchDashboardsRequest, opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { - console.log("here") try { const { detectorName } = request.params as { detectorName: string }; - const client = context.dataSource.opensearch.legacy.getClient('4585f560-d1ef-11ee-aa63-2181676cc573'); - const response = await client - .callAPI('ad.matchDetector', { - detectorName - }); + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const response = await callWithRequest( + 'ad.matchDetector', { + detectorName + }); + // const response = await this.client // .asScoped(request) // .callAsCurrentUser('ad.matchDetector', { @@ -1134,9 +1280,18 @@ export default class AdService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { try { - const response = await this.client - .asScoped(request) - .callAsCurrentUser('ad.detectorCount'); + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const response = await callWithRequest('ad.detectorCount'); + + // const response = await this.client + // .asScoped(request) + // .callAsCurrentUser('ad.detectorCount'); return opensearchDashboardsResponse.ok({ body: { ok: true, diff --git a/server/routes/alerting.ts b/server/routes/alerting.ts index 2616656d..794b973a 100644 --- a/server/routes/alerting.ts +++ b/server/routes/alerting.ts @@ -22,6 +22,7 @@ import { OpenSearchDashboardsResponseFactory, IOpenSearchDashboardsResponse, } from '../../../../src/core/server'; +import { getClientBasedOnDataSource } from '../utils/helpers'; export function registerAlertingRoutes( apiRouter: Router, @@ -33,9 +34,11 @@ export function registerAlertingRoutes( export default class AlertingService { private client: any; + dataSourceEnabled: boolean; - constructor(client: any) { + constructor(client: any, dataSourceEnabled: boolean) { this.client = client; + this.dataSourceEnabled = dataSourceEnabled; } searchMonitors = async ( @@ -71,9 +74,16 @@ export default class AlertingService { }, }, }; - const response: SearchResponse = await this.client - .asScoped(request) - .callAsCurrentUser('alerting.searchMonitors', { body: requestBody }); + + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const response: SearchResponse = await callWithRequest( + 'alerting.searchMonitors', { body: requestBody }); const totalMonitors = get(response, 'hits.total.value', 0); const allMonitors = get(response, 'hits.hits', []).reduce( (acc: any, monitor: any) => ({ @@ -123,9 +133,16 @@ export default class AlertingService { startTime?: number; endTime?: number; }; - const response = await this.client - .asScoped(request) - .callAsCurrentUser('alerting.searchAlerts', { + + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const response = await callWithRequest( + 'alerting.searchAlerts', { monitorId: monitorId, startTime: startTime, endTime: endTime, diff --git a/server/routes/opensearch.ts b/server/routes/opensearch.ts index 8a52c46e..b8f4ea0a 100644 --- a/server/routes/opensearch.ts +++ b/server/routes/opensearch.ts @@ -27,6 +27,7 @@ import { OpenSearchDashboardsResponseFactory, IOpenSearchDashboardsResponse, } from '../../../../src/core/server'; +import { getClientBasedOnDataSource } from '../utils/helpers'; type SearchParams = { index: string; @@ -49,9 +50,11 @@ export function registerOpenSearchRoutes( export default class OpenSearchService { private client: any; + dataSourceEnabled: boolean; - constructor(client: any) { + constructor(client: any, dataSourceEnabled: boolean) { this.client = client; + this.dataSourceEnabled = dataSourceEnabled; } executeSearch = async ( @@ -88,9 +91,14 @@ export default class OpenSearchService { const params: SearchParams = { index, size, body: requestBody }; - const results: SearchResponse = await this.client - .asScoped(request) - .callAsCurrentUser('search', params); + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const results: SearchResponse = await callWithRequest('search', params); return opensearchDashboardsResponse.ok({ body: { ok: true, response: results }, @@ -113,9 +121,15 @@ export default class OpenSearchService { ): Promise> => { const { index } = request.query as { index: string }; try { - const response: CatIndex[] = await this.client - .asScoped(request) - .callAsCurrentUser('cat.indices', { + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const response: CatIndex[] = await callWithRequest( + 'cat.indices', { index, format: 'json', h: 'health,index', @@ -150,9 +164,15 @@ export default class OpenSearchService { ): Promise> => { const { alias } = request.query as { alias: string }; try { - const response: IndexAlias[] = await this.client - .asScoped(request) - .callAsCurrentUser('cat.aliases', { + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const response: IndexAlias[] = await callWithRequest( + 'cat.aliases', { alias, format: 'json', h: 'alias,index', @@ -180,8 +200,14 @@ export default class OpenSearchService { const index = request.body.index; //@ts-ignore const body = request.body.body; + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); try { - await this.client.asScoped(request).callAsCurrentUser('indices.create', { + await callWithRequest('indices.create', { index: index, body: body, }); @@ -195,9 +221,7 @@ export default class OpenSearchService { }); } try { - const response: CatIndex[] = await this.client - .asScoped(request) - .callAsCurrentUser('cat.indices', { + const response: CatIndex[] = await callWithRequest('cat.indices', { index, format: 'json', h: 'health,index', @@ -223,9 +247,14 @@ export default class OpenSearchService { ): Promise> => { const body = request.body; try { - const response: any = await this.client - .asScoped(request) - .callAsCurrentUser('bulk', { + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const response: any = await callWithRequest('bulk', { body: body, }); return opensearchDashboardsResponse.ok({ @@ -248,8 +277,15 @@ export default class OpenSearchService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { const index = request.query as { index: string }; + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + try { - await this.client.asScoped(request).callAsCurrentUser('indices.delete', { + await callWithRequest('indices.delete', { index: index, }); } catch (err) { @@ -268,9 +304,8 @@ export default class OpenSearchService { } } try { - const response: CatIndex[] = await this.client - .asScoped(request) - .callAsCurrentUser('cat.indices', { + const response: CatIndex[] = await callWithRequest( + 'cat.indices', { index, format: 'json', h: 'health,index', @@ -296,9 +331,15 @@ export default class OpenSearchService { ): Promise> => { const { index } = request.query as { index: string }; try { - const response = await this.client - .asScoped(request) - .callAsCurrentUser('indices.getMapping', { + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + '4585f560-d1ef-11ee-aa63-2181676cc573', + this.client); + + const response = await callWithRequest( + 'indices.getMapping', { index, }); return opensearchDashboardsResponse.ok({ diff --git a/server/routes/sampleData.ts b/server/routes/sampleData.ts index 1874b944..e1881df2 100644 --- a/server/routes/sampleData.ts +++ b/server/routes/sampleData.ts @@ -35,9 +35,11 @@ export function registerSampleDataRoutes( export default class SampleDataService { private client: any; + dataSourceEnabled: boolean; - constructor(client: any) { + constructor(client: any, dataSourceEnabled: boolean) { this.client = client; + this.dataSourceEnabled = dataSourceEnabled; } // Get the zip file stored in server, unzip it, and bulk insert it diff --git a/server/utils/helpers.ts b/server/utils/helpers.ts index 6233a007..cde2e514 100644 --- a/server/utils/helpers.ts +++ b/server/utils/helpers.ts @@ -20,10 +20,7 @@ import { } from 'lodash'; import { MIN_IN_MILLI_SECS } from './constants'; -import { - LegacyCallAPIOptions, - OpenSearchDashboardsRequest, -} from '../../../../src/core/server'; +import { ILegacyCustomClusterClient, LegacyCallAPIOptions, OpenSearchDashboardsRequest } from '../../../../src/core/server'; export const SHOW_DECIMAL_NUMBER_THRESHOLD = 0.01; From 10c730b4a36c1f7a2fd65970af85bdc79f10c0b0 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Sun, 7 Apr 2024 08:47:46 -0700 Subject: [PATCH 09/26] Support MDS on DetectorDetails page Signed-off-by: Jackie Han --- public/models/interfaces.ts | 1 + .../hooks/useFetchDetectorInfo.ts | 3 +- .../containers/DetectorConfig.tsx | 3 +- .../containers/DetectorDetail.tsx | 52 +++-- .../containers/AnomalyHistory.tsx | 21 +- .../containers/AnomalyResults.tsx | 16 +- .../containers/AnomalyResultsLiveChart.tsx | 3 + .../pages/DetectorsList/utils/tableUtils.tsx | 8 +- .../containers/HistoricalDetectorResults.tsx | 11 +- public/pages/main/Main.tsx | 6 +- public/redux/reducers/ad.ts | 27 +-- public/redux/reducers/anomalyResults.ts | 13 +- public/redux/reducers/liveAnomalyResults.ts | 5 +- public/redux/reducers/opensearch.ts | 4 +- server/plugin.ts | 2 +- server/routes/ad.ts | 188 ++++++++++++------ server/routes/opensearch.ts | 29 ++- 17 files changed, 258 insertions(+), 134 deletions(-) diff --git a/public/models/interfaces.ts b/public/models/interfaces.ts index ecce16ef..9e0ec7da 100644 --- a/public/models/interfaces.ts +++ b/public/models/interfaces.ts @@ -205,6 +205,7 @@ export type Detector = { taskState?: DETECTOR_STATE; taskProgress?: number; taskError?: string; + dataSourceId?: string; }; export type DetectorListItem = { diff --git a/public/pages/CreateDetectorSteps/hooks/useFetchDetectorInfo.ts b/public/pages/CreateDetectorSteps/hooks/useFetchDetectorInfo.ts index af3cc4e9..2aaa4f37 100644 --- a/public/pages/CreateDetectorSteps/hooks/useFetchDetectorInfo.ts +++ b/public/pages/CreateDetectorSteps/hooks/useFetchDetectorInfo.ts @@ -43,7 +43,8 @@ export const useFetchDetectorInfo = ( useEffect(() => { const fetchDetector = async () => { if (!detector) { - await dispatch(getDetector(detectorId)); + // hardcoding the datasource id for now, will update it later when working on create page + await dispatch(getDetector(detectorId, '4585f560-d1ef-11ee-aa63-2181676cc573')); } if (selectedIndices) { await dispatch(getMappings(selectedIndices)); diff --git a/public/pages/DetectorConfig/containers/DetectorConfig.tsx b/public/pages/DetectorConfig/containers/DetectorConfig.tsx index e829fef4..6cd37104 100644 --- a/public/pages/DetectorConfig/containers/DetectorConfig.tsx +++ b/public/pages/DetectorConfig/containers/DetectorConfig.tsx @@ -21,6 +21,7 @@ import { getDetector } from '../../../redux/reducers/ad'; import { EuiLoadingSpinner } from '@elastic/eui'; interface DetectorConfigProps extends RouteComponentProps { detectorId: string; + dataSourceId: string; onEditFeatures(): void; onEditDetector(): void; } @@ -32,7 +33,7 @@ export function DetectorConfig(props: DetectorConfigProps) { ); useEffect(() => { - dispatch(getDetector(props.detectorId)); + dispatch(getDetector(props.detectorId, props.dataSourceId)); }, []); return ( diff --git a/public/pages/DetectorDetail/containers/DetectorDetail.tsx b/public/pages/DetectorDetail/containers/DetectorDetail.tsx index 20f7344f..669b0da2 100644 --- a/public/pages/DetectorDetail/containers/DetectorDetail.tsx +++ b/public/pages/DetectorDetail/containers/DetectorDetail.tsx @@ -24,10 +24,10 @@ import { EuiLoadingSpinner, EuiButton, } from '@elastic/eui'; -import { CoreStart } from '../../../../../../src/core/public'; +import { CoreStart, MountPoint } from '../../../../../../src/core/public'; import { CoreServicesContext } from '../../../components/CoreServices/CoreServices'; import { get, isEmpty } from 'lodash'; -import { RouteComponentProps, Switch, Route, Redirect } from 'react-router-dom'; +import { RouteComponentProps, Switch, Route, Redirect, useLocation } from 'react-router-dom'; import { useDispatch, useSelector } from 'react-redux'; import { useFetchDetectorInfo } from '../../CreateDetectorSteps/hooks/useFetchDetectorInfo'; import { useHideSideNavBar } from '../../main/hooks/useHideSideNavBar'; @@ -58,12 +58,16 @@ import { import { DETECTOR_STATE } from '../../../../server/utils/constants'; import { CatIndex } from '../../../../server/models/types'; import { containsIndex } from '../utils/helpers'; +import { DataSourceManagementPluginSetup, DataSourceViewConfig } from '../../../../../../src/plugins/data_source_management/public'; export interface DetectorRouterProps { detectorId?: string; } -interface DetectorDetailProps - extends RouteComponentProps {} +interface DetectorDetailProps extends RouteComponentProps { + dataSourceEnabled: boolean; + dataSourceManagement: DataSourceManagementPluginSetup; + setActionMenu: (menuMount: MountPoint | undefined) => void; +} const tabs = [ { @@ -103,6 +107,11 @@ export const DetectorDetail = (props: DetectorDetailProps) => { const core = React.useContext(CoreServicesContext) as CoreStart; const dispatch = useDispatch(); const detectorId = get(props, 'match.params.detectorId', '') as string; + + const location = useLocation(); + const queryParams = new URLSearchParams(location.search); + const dataSourceId = queryParams.get('dataSourceId') as string; + const { detector, hasError, isLoadingDetector, errorMessage } = useFetchDetectorInfo(detectorId); const { monitor, fetchMonitorError, isLoadingMonitor } = @@ -156,7 +165,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { // detector starts, result index recreated or user switches tabs to re-fetch detector) useEffect(() => { const getInitialIndices = async () => { - await dispatch(getIndices('')).catch((error: any) => { + await dispatch(getIndices('', dataSourceId)).catch((error: any) => { console.error(error); core.notifications.toasts.addDanger('Error getting all indices'); }); @@ -201,7 +210,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { ...detectorDetailModel, selectedTab: DETECTOR_DETAIL_TABS.CONFIGURATIONS, }); - props.history.push(`/detectors/${detectorId}/configurations`); + props.history.push(`/detectors/${detectorId}/configurations?dataSourceId=${dataSourceId}`); }, []); const handleSwitchToHistoricalTab = useCallback(() => { @@ -209,7 +218,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { ...detectorDetailModel, selectedTab: DETECTOR_DETAIL_TABS.HISTORICAL, }); - props.history.push(`/detectors/${detectorId}/historical`); + props.history.push(`/detectors/${detectorId}/historical?dataSourceId=${dataSourceId}`); }, []); const handleTabChange = (route: DETECTOR_DETAIL_TABS) => { @@ -217,7 +226,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { ...detectorDetailModel, selectedTab: route, }); - props.history.push(`/detectors/${detectorId}/${route}`); + props.history.push(`/detectors/${detectorId}/${route}?dataSourceId=${dataSourceId}`); }; const hideMonitorCalloutModal = () => { @@ -261,8 +270,8 @@ export const DetectorDetail = (props: DetectorDetailProps) => { // Await for the start detector call to succeed before displaying toast. // Don't wait for get detector call; the page will be updated // via hooks automatically when the new detector info is returned. - await dispatch(startDetector(detectorId)); - dispatch(getDetector(detectorId)); + await dispatch(startDetector(detectorId, dataSourceId)); + dispatch(getDetector(detectorId, dataSourceId)); core.notifications.toasts.addSuccess( `Successfully started the detector job` ); @@ -278,10 +287,10 @@ export const DetectorDetail = (props: DetectorDetailProps) => { const handleStopAdJob = async (detectorId: string, listener?: Listener) => { try { if (isRTJobRunning) { - await dispatch(stopDetector(detectorId)); + await dispatch(stopDetector(detectorId, dataSourceId)); } if (isHistoricalJobRunning) { - await dispatch(stopHistoricalDetector(detectorId)); + await dispatch(stopHistoricalDetector(detectorId, dataSourceId)); } core.notifications.toasts.addSuccess( `Successfully stopped the ${runningJobsAsString}` @@ -302,7 +311,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { const handleDelete = useCallback(async (detectorId: string) => { try { - await dispatch(deleteDetector(detectorId)); + await dispatch(deleteDetector(detectorId, dataSourceId)); core.notifications.toasts.addSuccess(`Successfully deleted the detector`); hideDeleteDetectorModal(); props.history.push('/detectors'); @@ -350,6 +359,8 @@ export const DetectorDetail = (props: DetectorDetailProps) => { > ) : null; + const DataSourceMenu = props.dataSourceManagement.ui.getDataSourceMenu(); + return ( {!isEmpty(detector) && !hasError ? ( @@ -361,6 +372,18 @@ export const DetectorDetail = (props: DetectorDetailProps) => { : { ...lightStyles, flexGrow: 'unset' }), }} > + + {props.dataSourceEnabled && ( + + )} { handleStartAdJob(detectorId)} onStopDetector={() => handleStopAdJob(detectorId)} onSwitchToConfiguration={handleSwitchToConfigurationTab} @@ -556,6 +580,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { )} /> @@ -566,6 +591,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { diff --git a/public/pages/DetectorResults/containers/AnomalyHistory.tsx b/public/pages/DetectorResults/containers/AnomalyHistory.tsx index e9a355df..219270ec 100644 --- a/public/pages/DetectorResults/containers/AnomalyHistory.tsx +++ b/public/pages/DetectorResults/containers/AnomalyHistory.tsx @@ -98,6 +98,7 @@ import { prettifyErrorMessage } from '../../../../server/utils/helpers'; interface AnomalyHistoryProps { detector: Detector; + dataSourceId: string; monitor: Monitor | undefined; isFeatureDataMissing?: boolean; isHistorical?: boolean; @@ -126,6 +127,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { props.isHistorical && props.detector?.detectionDateRange ? props.detector.detectionDateRange.endTime : moment().valueOf(); + const dataSourceId = props.dataSourceId; const [dateRange, setDateRange] = useState({ startDate: initialStartDate, endDate: initialEndDate, @@ -223,7 +225,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { taskId.current, modelId ); - return dispatch(searchResults(params, resultIndex, true)); + return dispatch(searchResults(params, resultIndex, dataSourceId, true)); }) : []; @@ -252,7 +254,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { modelId ); const anomalySummaryResponse = await dispatch( - searchResults(anomalySummaryQuery, resultIndex, true) + searchResults(anomalySummaryQuery, resultIndex, dataSourceId, true) ); allPureAnomalies.push(parsePureAnomalies(anomalySummaryResponse)); } @@ -275,7 +277,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { taskId.current, modelId ); - return dispatch(searchResults(params, resultIndex, true)); + return dispatch(searchResults(params, resultIndex, dataSourceId, true)); } ); @@ -308,7 +310,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { modelId ); const bucketizedAnomalyResultResponse = await dispatch( - searchResults(bucketizedAnomalyResultsQuery, resultIndex, true) + searchResults(bucketizedAnomalyResultsQuery, resultIndex, dataSourceId, true) ); allBucketizedAnomalyResults.push( parseBucketizedAnomalyResults(bucketizedAnomalyResultResponse) @@ -408,7 +410,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { ); const detectorResultResponse = props.isHistorical ? await dispatch( - getDetectorResults(taskId.current, params, true, resultIndex, true) + getDetectorResults(taskId.current, dataSourceId, params, true, resultIndex, true) ).catch((error: any) => { setIsLoading(false); setIsLoadingAnomalyResults(false); @@ -417,6 +419,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { : await dispatch( getDetectorResults( props.detector.id, + dataSourceId, params, false, resultIndex, @@ -536,6 +539,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { const result = await dispatch( getTopAnomalyResults( detectorId, + dataSourceId, get(props, 'isHistorical', false), query ) @@ -553,7 +557,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { props.isHistorical, taskId.current ); - const result = await dispatch(searchResults(query, resultIndex, true)); + const result = await dispatch(searchResults(query, resultIndex, dataSourceId, true)); topEntityAnomalySummaries = parseTopEntityAnomalySummaryResults( result, isMultiCategory @@ -572,7 +576,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { props.isHistorical, taskId.current ); - return dispatch(searchResults(entityResultQuery, resultIndex, true)); + return dispatch(searchResults(entityResultQuery, resultIndex, dataSourceId, true)); } ); @@ -644,6 +648,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { return dispatch( getDetectorResults( props.isHistorical ? taskId.current : props.detector?.id, + dataSourceId, params, props.isHistorical ? true : false, resultIndex, @@ -722,7 +727,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { heatmapCell.entityList ); - const result = await dispatch(searchResults(query, resultIndex, true)); + const result = await dispatch(searchResults(query, resultIndex, dataSourceId, true)); // Gets top child entities as an Entity[][], // where each entry in the array is a unique combination of entity values diff --git a/public/pages/DetectorResults/containers/AnomalyResults.tsx b/public/pages/DetectorResults/containers/AnomalyResults.tsx index 827570ce..7d831ab3 100644 --- a/public/pages/DetectorResults/containers/AnomalyResults.tsx +++ b/public/pages/DetectorResults/containers/AnomalyResults.tsx @@ -70,6 +70,7 @@ import { DEFAULT_SHINGLE_SIZE } from '../../../utils/constants'; interface AnomalyResultsProps extends RouteComponentProps { detectorId: string; + dataSourceId: string; onStartDetector(): void; onStopDetector(): void; onSwitchToConfiguration(): void; @@ -80,7 +81,8 @@ export function AnomalyResults(props: AnomalyResultsProps) { const core = React.useContext(CoreServicesContext) as CoreStart; const dispatch = useDispatch(); const detectorId = props.detectorId; - const detector = useSelector( + const dataSourceId = props.dataSourceId; + const detector = useSelector( (state: AppState) => state.ad.detectors[detectorId] ); @@ -90,11 +92,11 @@ export function AnomalyResults(props: AnomalyResultsProps) { BREADCRUMBS.DETECTORS, { text: detector ? detector.name : '' }, ]); - dispatch(getDetector(detectorId)); + dispatch(getDetector(detectorId, dataSourceId)); }, []); const fetchDetector = async () => { - dispatch(getDetector(detectorId)); + dispatch(getDetector(detectorId, dataSourceId)); }; useEffect(() => { @@ -247,7 +249,7 @@ export function AnomalyResults(props: AnomalyResultsProps) { try { const resultIndex = get(detector, 'resultIndex', ''); const detectorResultResponse = await dispatch( - getDetectorResults(detectorId, params, false, resultIndex, true) + getDetectorResults(detectorId, dataSourceId, params, false, resultIndex, true) ); const featuresData = get( detectorResultResponse, @@ -550,10 +552,14 @@ export function AnomalyResults(props: AnomalyResultsProps) {
    ) : null} - + ( - - {name} - + + {name} + ), }, { @@ -112,7 +112,7 @@ export const staticColumn = [ width: '15%', render: (name: string, detector: Detector) => { return !isEmpty(detector.taskId) ? ( - + View results ) : ( diff --git a/public/pages/HistoricalDetectorResults/containers/HistoricalDetectorResults.tsx b/public/pages/HistoricalDetectorResults/containers/HistoricalDetectorResults.tsx index bc8e80e3..879a153e 100644 --- a/public/pages/HistoricalDetectorResults/containers/HistoricalDetectorResults.tsx +++ b/public/pages/HistoricalDetectorResults/containers/HistoricalDetectorResults.tsx @@ -53,6 +53,7 @@ import { HistoricalDetectorCallout } from '../components/HistoricalDetectorCallo interface HistoricalDetectorResultsProps extends RouteComponentProps { detectorId: string; + dataSourceId: string; } export function HistoricalDetectorResults( @@ -62,6 +63,7 @@ export function HistoricalDetectorResults( const isDark = darkModeEnabled(); const dispatch = useDispatch(); const detectorId: string = get(props, 'match.params.detectorId', ''); + const dataSourceId = props.dataSourceId; const adState = useSelector((state: AppState) => state.ad); const allDetectors = adState.detectors; @@ -81,7 +83,7 @@ export function HistoricalDetectorResults( const fetchDetector = async () => { try { - await dispatch(getDetector(detectorId)); + await dispatch(getDetector(detectorId, dataSourceId)); } catch {} }; @@ -113,7 +115,7 @@ export function HistoricalDetectorResults( const startHistoricalTask = async (startTime: number, endTime: number) => { try { - dispatch(startHistoricalDetector(props.detectorId, startTime, endTime)) + dispatch(startHistoricalDetector(props.detectorId, dataSourceId, startTime, endTime)) .then((response: any) => { setTaskId(get(response, 'response._id')); core.notifications.toasts.addSuccess( @@ -141,9 +143,9 @@ export function HistoricalDetectorResults( const onStopDetector = async () => { try { setIsStoppingDetector(true); - await dispatch(stopHistoricalDetector(detectorId)); + await dispatch(stopHistoricalDetector(detectorId, dataSourceId)); await waitForMs(HISTORICAL_DETECTOR_STOP_THRESHOLD); - dispatch(getDetector(detectorId)).then((response: any) => { + dispatch(getDetector(detectorId, dataSourceId)).then((response: any) => { if (get(response, 'response.curState') !== DETECTOR_STATE.DISABLED) { throw 'please try again.'; } else { @@ -254,6 +256,7 @@ export function HistoricalDetectorResults( ( - + )} /> ({ +export const getDetector = (detectorId: string, dataSourceId: string): APIAction => ({ type: GET_DETECTOR, - request: (client: HttpSetup) => - client.get(`..${AD_NODE_API.DETECTOR}/${detectorId}`), - detectorId, + request: (client: HttpSetup) => + client.get(`..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}`), + detectorId, dataSourceId }); export const getDetectorList = ( @@ -423,28 +423,29 @@ export const updateDetector = ( detectorId, }); -export const deleteDetector = (detectorId: string): APIAction => ({ +export const deleteDetector = (detectorId: string, dataSourceId: string): APIAction => ({ type: DELETE_DETECTOR, request: (client: HttpSetup) => - client.delete(`..${AD_NODE_API.DETECTOR}/${detectorId}`), + client.delete(`..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}`), detectorId, }); -export const startDetector = (detectorId: string): APIAction => ({ +export const startDetector = (detectorId: string, dataSourceId: string): APIAction => ({ type: START_DETECTOR, request: (client: HttpSetup) => - client.post(`..${AD_NODE_API.DETECTOR}/${detectorId}/start`), + client.post(`..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}/start`), detectorId, }); export const startHistoricalDetector = ( detectorId: string, + dataSourceId: string, startTime: number, endTime: number ): APIAction => ({ type: START_HISTORICAL_DETECTOR, request: (client: HttpSetup) => - client.post(`..${AD_NODE_API.DETECTOR}/${detectorId}/start`, { + client.post(`..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}/start`, { body: JSON.stringify({ startTime: startTime, endTime: endTime, @@ -455,17 +456,17 @@ export const startHistoricalDetector = ( endTime, }); -export const stopDetector = (detectorId: string): APIAction => ({ +export const stopDetector = (detectorId: string, dataSourceId: string): APIAction => ({ type: STOP_DETECTOR, request: (client: HttpSetup) => - client.post(`..${AD_NODE_API.DETECTOR}/${detectorId}/stop/${false}`), + client.post(`..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}/stop/${false}`), detectorId, }); -export const stopHistoricalDetector = (detectorId: string): APIAction => ({ +export const stopHistoricalDetector = (detectorId: string, dataSourceId: string): APIAction => ({ type: STOP_HISTORICAL_DETECTOR, request: (client: HttpSetup) => - client.post(`..${AD_NODE_API.DETECTOR}/${detectorId}/stop/${true}`), + client.post(`..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}/stop/${true}`), detectorId, }); diff --git a/public/redux/reducers/anomalyResults.ts b/public/redux/reducers/anomalyResults.ts index 4c5d5f50..2d8b50cd 100644 --- a/public/redux/reducers/anomalyResults.ts +++ b/public/redux/reducers/anomalyResults.ts @@ -94,6 +94,7 @@ const reducer = handleActions( export const getDetectorResults = ( id: string, + dataSourceId: string, queryParams: any, isHistorical: boolean, resultIndex: string, @@ -104,7 +105,7 @@ export const getDetectorResults = ( type: DETECTOR_RESULTS, request: (client: HttpSetup) => client.get( - `..${AD_NODE_API.DETECTOR}/${id}/results/${isHistorical}`, + `..${AD_NODE_API.DETECTOR}/${id}/${dataSourceId}/results/${isHistorical}`, { query: queryParams, } @@ -114,7 +115,7 @@ export const getDetectorResults = ( type: DETECTOR_RESULTS, request: (client: HttpSetup) => client.get( - `..${AD_NODE_API.DETECTOR}/${id}/results/${isHistorical}/${resultIndex}/${onlyQueryCustomResultIndex}`, + `..${AD_NODE_API.DETECTOR}/${id}/${dataSourceId}/results/${isHistorical}/${resultIndex}/${onlyQueryCustomResultIndex}`, { query: queryParams, } @@ -124,13 +125,14 @@ export const getDetectorResults = ( export const searchResults = ( requestBody: any, resultIndex: string, + dataSourceId: string, onlyQueryCustomResultIndex: boolean ): APIAction => !resultIndex ? { type: SEARCH_ANOMALY_RESULTS, request: (client: HttpSetup) => - client.post(`..${AD_NODE_API.DETECTOR}/results/_search`, { + client.post(`..${AD_NODE_API.DETECTOR}/results/${dataSourceId}/_search`, { body: JSON.stringify(requestBody), }), } @@ -138,7 +140,7 @@ export const searchResults = ( type: SEARCH_ANOMALY_RESULTS, request: (client: HttpSetup) => client.post( - `..${AD_NODE_API.DETECTOR}/results/_search/${resultIndex}/${onlyQueryCustomResultIndex}`, + `..${AD_NODE_API.DETECTOR}/results/${dataSourceId}/_search/${resultIndex}/${onlyQueryCustomResultIndex}`, { body: JSON.stringify(requestBody), } @@ -147,13 +149,14 @@ export const searchResults = ( export const getTopAnomalyResults = ( detectorId: string, + dataSourceId: string, isHistorical: boolean, requestBody: any ): APIAction => ({ type: GET_TOP_ANOMALY_RESULTS, request: (client: HttpSetup) => client.post( - `..${AD_NODE_API.DETECTOR}/${detectorId}/_topAnomalies/${isHistorical}`, + `..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}/_topAnomalies/${isHistorical}`, { body: JSON.stringify(requestBody), } diff --git a/public/redux/reducers/liveAnomalyResults.ts b/public/redux/reducers/liveAnomalyResults.ts index a41f7f2d..190dba91 100644 --- a/public/redux/reducers/liveAnomalyResults.ts +++ b/public/redux/reducers/liveAnomalyResults.ts @@ -57,6 +57,7 @@ const reducer = handleActions( export const getDetectorLiveResults = ( detectorId: string, + dataSourceId: string, queryParams: DetectorResultsQueryParams, isHistorical: boolean, resultIndex: string, @@ -67,7 +68,7 @@ export const getDetectorLiveResults = ( type: DETECTOR_LIVE_RESULTS, request: (client: HttpSetup) => client.get( - `..${AD_NODE_API.DETECTOR}/${detectorId}/results/${isHistorical}`, + `..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}/results/${isHistorical}`, { query: queryParams, } @@ -77,7 +78,7 @@ export const getDetectorLiveResults = ( type: DETECTOR_LIVE_RESULTS, request: (client: HttpSetup) => client.get( - `..${AD_NODE_API.DETECTOR}/${detectorId}/results/${isHistorical}/${resultIndex}/${onlyQueryCustomResultIndex}`, + `..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}/results/${isHistorical}/${resultIndex}/${onlyQueryCustomResultIndex}`, { query: queryParams, } diff --git a/public/redux/reducers/opensearch.ts b/public/redux/reducers/opensearch.ts index 17a807b4..a2856edc 100644 --- a/public/redux/reducers/opensearch.ts +++ b/public/redux/reducers/opensearch.ts @@ -246,10 +246,10 @@ const reducer = handleActions( initialState ); -export const getIndices = (searchKey: string = ''): APIAction => ({ +export const getIndices = (searchKey: string = '', dataSourceId: string): APIAction => ({ type: GET_INDICES, request: (client: HttpSetup) => - client.get(`..${AD_NODE_API._INDICES}`, { query: { index: searchKey } }), + client.get(`..${AD_NODE_API._INDICES}/${dataSourceId}`, { query: { index: searchKey } }), }); export const getAliases = (searchKey: string = ''): APIAction => ({ diff --git a/server/plugin.ts b/server/plugin.ts index 8c446e36..97f644da 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -89,7 +89,7 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin // Create services & register with OpenSearch client const adService = new AdService(client, dataSourceEnabled); const alertingService = new AlertingService(client); - const opensearchService = new OpenSearchService(client); + const opensearchService = new OpenSearchService(client, dataSourceEnabled); const sampleDataService = new SampleDataService(client); // Register server routes with the service diff --git a/server/routes/ad.ts b/server/routes/ad.ts index 9b5968dd..4d83a462 100644 --- a/server/routes/ad.ts +++ b/server/routes/ad.ts @@ -64,36 +64,37 @@ export function registerADRoutes(apiRouter: Router, adService: AdService) { apiRouter.put('/detectors/{detectorId}', adService.putDetector); apiRouter.post('/detectors/_search', adService.searchDetector); apiRouter.post('/detectors/results/_search/', adService.searchResults); - apiRouter.post('/detectors/results/_search', adService.searchResults); + apiRouter.post('/detectors/results/{dataSourceId}/_search', adService.searchResults); apiRouter.post( - '/detectors/results/_search/{resultIndex}/{onlyQueryCustomResultIndex}', + '/detectors/results/{dataSourceId}/_search/{resultIndex}/{onlyQueryCustomResultIndex}', adService.searchResults ); - apiRouter.get('/detectors/{detectorId}', adService.getDetector); apiRouter.get('/detectors', adService.getDetectors); apiRouter.post('/detectors/preview', adService.previewDetector); apiRouter.get( - '/detectors/{id}/results/{isHistorical}/{resultIndex}/{onlyQueryCustomResultIndex}', + '/detectors/{id}/{dataSourceId}/results/{isHistorical}/{resultIndex}/{onlyQueryCustomResultIndex}', adService.getAnomalyResults ); apiRouter.get( - '/detectors/{id}/results/{isHistorical}', + '/detectors/{id}/{dataSourceId}/results/{isHistorical}', adService.getAnomalyResults ); - apiRouter.delete('/detectors/{detectorId}', adService.deleteDetector); - apiRouter.post('/detectors/{detectorId}/start', adService.startDetector); + apiRouter.delete('/detectors/{detectorId}/{dataSourceId}', adService.deleteDetector); + apiRouter.post('/detectors/{detectorId}/{dataSourceId}/start', adService.startDetector); apiRouter.post( - '/detectors/{detectorId}/stop/{isHistorical}', + '/detectors/{detectorId}/{dataSourceId}/stop/{isHistorical}', adService.stopDetector ); apiRouter.get( '/detectors/{detectorId}/_profile', adService.getDetectorProfile ); + apiRouter.get('/detectors/{detectorId}/{dataSourceId}', adService.getDetector); + apiRouter.get('/detectors/{detectorName}/_match', adService.matchDetector); apiRouter.get('/detectors/_count', adService.getDetectorCount); apiRouter.post( - '/detectors/{detectorId}/_topAnomalies/{isHistorical}', + '/detectors/{detectorId}/{dataSourceId}/_topAnomalies/{isHistorical}', adService.getTopAnomalyResults ); apiRouter.post( @@ -118,11 +119,19 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; - const response = await this.client - .asScoped(request) - .callAsCurrentUser('ad.deleteDetector', { + const { dataSourceId } = request.params as { dataSourceId: string }; + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + dataSourceId, + this.client); + + const response = await callWithRequest( + 'ad.deleteDetector', { detectorId, }); + return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -272,10 +281,17 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; - const detectorResponse = await this.client - .asScoped(request) - .callAsCurrentUser('ad.getDetector', { - detectorId, + const { dataSourceId } = request.params as { dataSourceId: string }; + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + dataSourceId, + this.client); + + const detectorResponse = await callWithRequest( + 'ad.getDetector', { + detectorId, }); // Populating static detector fields @@ -293,16 +309,22 @@ export default class AdService { let realtimeTasksResponse = {} as any; let historicalTasksResponse = {} as any; try { - realtimeTasksResponse = await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchTasks', { - body: getLatestTaskForDetectorQuery(detectorId, true), + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + dataSourceId, + this.client); + + realtimeTasksResponse = await callWithRequest( + 'ad.searchTasks', { + body: getLatestTaskForDetectorQuery(detectorId, true), }); - historicalTasksResponse = await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchTasks', { + + historicalTasksResponse = await callWithRequest('ad.searchTasks', { body: getLatestTaskForDetectorQuery(detectorId, false), }); + } catch (err) { if (!isIndexNotFoundError(err)) { throw err; @@ -367,6 +389,7 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; + const { dataSourceId } = request.params as { dataSourceId: string }; //@ts-ignore const startTime = request.body?.startTime; //@ts-ignore @@ -385,9 +408,15 @@ export default class AdService { requestPath = 'ad.startHistoricalDetector'; } - const response = await this.client - .asScoped(request) - .callAsCurrentUser(requestPath, requestParams); + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + dataSourceId, + this.client); + + const response = await callWithRequest(requestPath, requestParams); + return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -411,8 +440,9 @@ export default class AdService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { try { - let { detectorId, isHistorical } = request.params as { + let { detectorId, dataSourceId, isHistorical } = request.params as { detectorId: string; + dataSourceId: string; isHistorical: any; }; isHistorical = JSON.parse(isHistorical) as boolean; @@ -420,11 +450,18 @@ export default class AdService { ? 'ad.stopHistoricalDetector' : 'ad.stopDetector'; - const response = await this.client - .asScoped(request) - .callAsCurrentUser(requestPath, { + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + dataSourceId, + this.client); + + const response = await callWithRequest( + requestPath, { detectorId, }); + return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -519,8 +556,9 @@ export default class AdService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { try { - var { resultIndex, onlyQueryCustomResultIndex } = request.params as { + var { resultIndex, dataSourceId, onlyQueryCustomResultIndex } = request.params as { resultIndex: string; + dataSourceId: string; onlyQueryCustomResultIndex: boolean; }; if ( @@ -535,15 +573,19 @@ export default class AdService { onlyQueryCustomResultIndex: onlyQueryCustomResultIndex, } as {}; const requestBody = JSON.stringify(request.body); + + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + dataSourceId, + this.client); + const response = !resultIndex - ? await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchResults', { + ? await callWithRequest('ad.searchResults', { body: requestBody, }) - : await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchResultsFromCustomResultIndex', { + : await callWithRequest('ad.searchResultsFromCustomResultIndex', { ...requestParams, body: requestBody, }); @@ -582,7 +624,7 @@ export default class AdService { indices = '', sortDirection = SORT_DIRECTION.DESC, sortField = 'name', - dataSourceId = '', + dataSourceId = '' } = request.query as GetDetectorsQueryParams; const mustQueries = []; if (search.trim()) { @@ -625,16 +667,15 @@ export default class AdService { }, }, }; + const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client - ); - const response = await callWithRequest('ad.searchDetector', { - body: requestBody, - }); + this.client); + const response = await callWithRequest('ad.searchDetector', { body: requestBody }); + const totalDetectors = get(response, 'hits.total.value', 0); //Get all detectors from search detector API @@ -665,9 +706,9 @@ export default class AdService { resultIndex: CUSTOM_AD_RESULT_INDEX_PREFIX + '*', onlyQueryCustomResultIndex: 'false', } as {}; + const aggregationResult = await callWithRequest( - 'ad.searchResultsFromCustomResultIndex', - { + 'ad.searchResultsFromCustomResultIndex', { ...requestParams, body: getResultAggregationQuery(allDetectorIds, { from, @@ -677,8 +718,7 @@ export default class AdService { search, indices, }), - } - ); + }); const aggsDetectors = get( aggregationResult, 'aggregations.unique_detectors.buckets', @@ -731,12 +771,15 @@ export default class AdService { let realtimeTasksResponse = {} as any; let historicalTasksResponse = {} as any; try { - realtimeTasksResponse = await callWithRequest('ad.searchTasks', { - body: getLatestDetectorTasksQuery(true), + realtimeTasksResponse = await callWithRequest( + 'ad.searchTasks', { + body: getLatestDetectorTasksQuery(true), }); - historicalTasksResponse = await callWithRequest('ad.searchTasks', { + historicalTasksResponse = await callWithRequest( + 'ad.searchTasks', { body: getLatestDetectorTasksQuery(false), }); + } catch (err) { if (!isIndexNotFoundError(err)) { throw err; @@ -816,9 +859,10 @@ export default class AdService { request: OpenSearchDashboardsRequest, opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { - let { id, isHistorical, resultIndex, onlyQueryCustomResultIndex } = + let { id, dataSourceId, isHistorical, resultIndex, onlyQueryCustomResultIndex } = request.params as { id: string; + dataSourceId: string; isHistorical: any; resultIndex: string; onlyQueryCustomResultIndex: boolean; @@ -953,19 +997,25 @@ export default class AdService { resultIndex: resultIndex, onlyQueryCustomResultIndex: onlyQueryCustomResultIndex, } as {}; + + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + dataSourceId, + this.client); + const response = !resultIndex - ? await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchResults', { + ? await callWithRequest( + 'ad.searchResults', { body: requestBody, }) - : await this.client - .asScoped(request) - .callAsCurrentUser('ad.searchResultsFromCustomResultIndex', { + : await callWithRequest( + 'ad.searchResultsFromCustomResultIndex', { ...requestParams, body: requestBody, }); - + const totalResults: number = get(response, 'hits.total.value', 0); const detectorResult: AnomalyResult[] = []; @@ -1049,8 +1099,9 @@ export default class AdService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { try { - let { detectorId, isHistorical } = request.params as { + let { detectorId, dataSourceId, isHistorical } = request.params as { detectorId: string; + dataSourceId: string; isHistorical: any; }; isHistorical = JSON.parse(isHistorical) as boolean; @@ -1058,9 +1109,16 @@ export default class AdService { ? 'ad.topHistoricalAnomalyResults' : 'ad.topAnomalyResults'; - const response = await this.client - .asScoped(request) - .callAsCurrentUser(requestPath, { + + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + dataSourceId, + this.client); + + const response = await callWithRequest( + requestPath, { detectorId: detectorId, body: request.body, }); diff --git a/server/routes/opensearch.ts b/server/routes/opensearch.ts index 8a52c46e..9f029195 100644 --- a/server/routes/opensearch.ts +++ b/server/routes/opensearch.ts @@ -27,6 +27,7 @@ import { OpenSearchDashboardsResponseFactory, IOpenSearchDashboardsResponse, } from '../../../../src/core/server'; +import { getClientBasedOnDataSource } from '../utils/helpers'; type SearchParams = { index: string; @@ -38,7 +39,7 @@ export function registerOpenSearchRoutes( apiRouter: Router, opensearchService: OpenSearchService ) { - apiRouter.get('/_indices', opensearchService.getIndices); + apiRouter.get('/_indices/{dataSourceId}', opensearchService.getIndices); apiRouter.get('/_aliases', opensearchService.getAliases); apiRouter.get('/_mappings', opensearchService.getMapping); apiRouter.post('/_search', opensearchService.executeSearch); @@ -49,9 +50,11 @@ export function registerOpenSearchRoutes( export default class OpenSearchService { private client: any; + dataSourceEnabled: boolean; - constructor(client: any) { + constructor(client: any, dataSourceEnabled: boolean) { this.client = client; + this.dataSourceEnabled = dataSourceEnabled; } executeSearch = async ( @@ -112,14 +115,22 @@ export default class OpenSearchService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { const { index } = request.query as { index: string }; + const { dataSourceId } = request.params as { dataSourceId: string }; try { - const response: CatIndex[] = await this.client - .asScoped(request) - .callAsCurrentUser('cat.indices', { - index, - format: 'json', - h: 'health,index', - }); + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + dataSourceId, + this.client); + + const response: CatIndex[] = await callWithRequest( + 'cat.indices', { + index, + format: 'json', + h: 'health,index', + }); + return opensearchDashboardsResponse.ok({ body: { ok: true, response: { indices: response } }, }); From fd26997a6aec8bcfc1bf1d06bf884caaacd9db47 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Sun, 7 Apr 2024 22:37:49 -0700 Subject: [PATCH 10/26] change 4/7 Signed-off-by: Jackie Han --- .../Dashboard/Container/DashboardOverview.tsx | 32 ++- server/routes/ad.ts | 271 +++++------------- 2 files changed, 96 insertions(+), 207 deletions(-) diff --git a/public/pages/Dashboard/Container/DashboardOverview.tsx b/public/pages/Dashboard/Container/DashboardOverview.tsx index 2bf95331..9c8c3b9e 100644 --- a/public/pages/Dashboard/Container/DashboardOverview.tsx +++ b/public/pages/Dashboard/Container/DashboardOverview.tsx @@ -9,7 +9,7 @@ * GitHub history for details. */ -import React, { Fragment, useState, useEffect } from 'react'; +import React, { Fragment, useState, useEffect, useMemo } from 'react'; import { AnomaliesLiveChart } from '../Components/AnomaliesLiveChart'; import { AnomaliesDistributionChart } from '../Components/AnomaliesDistribution'; import queryString from 'querystring'; @@ -237,22 +237,28 @@ export function DashboardOverview(props: OverviewProps) { ); }, [selectedDetectorsName, selectedIndices, selectedDetectorStates]); - const DataSourceMenu = props.dataSourceManagement.ui.getDataSourceMenu(); + const DataSourceMenu = + props.dataSourceManagement.ui.getDataSourceMenu(); + const renderDataSourceComponent = useMemo(() => { + return ( + + handleDataSourceChange(dataSources), + }} + /> + ); + }, [getSavedObjectsClient(), getNotifications(), props.setActionMenu]); return (
    - handleDataSourceChange(dataSources), - }} - /> + {renderDataSourceComponent} 0} /> {isLoadingDetectors ? (
    diff --git a/server/routes/ad.ts b/server/routes/ad.ts index 8016c443..e13d163b 100644 --- a/server/routes/ad.ts +++ b/server/routes/ad.ts @@ -16,7 +16,6 @@ import { Detector, GetDetectorsQueryParams, FeatureResult, - MDSQueryParams, } from '../models/types'; import { Router } from '../router'; import { @@ -65,36 +64,37 @@ export function registerADRoutes(apiRouter: Router, adService: AdService) { apiRouter.put('/detectors/{detectorId}', adService.putDetector); apiRouter.post('/detectors/_search', adService.searchDetector); apiRouter.post('/detectors/results/_search/', adService.searchResults); - apiRouter.post('/detectors/results/_search', adService.searchResults); + apiRouter.post('/detectors/results/{dataSourceId}/_search', adService.searchResults); apiRouter.post( - '/detectors/results/_search/{resultIndex}/{onlyQueryCustomResultIndex}', + '/detectors/results/{dataSourceId}/_search/{resultIndex}/{onlyQueryCustomResultIndex}', adService.searchResults ); - apiRouter.get('/detectors/{detectorId}', adService.getDetector); apiRouter.get('/detectors', adService.getDetectors); apiRouter.post('/detectors/preview', adService.previewDetector); apiRouter.get( - '/detectors/{id}/results/{isHistorical}/{resultIndex}/{onlyQueryCustomResultIndex}', + '/detectors/{id}/{dataSourceId}/results/{isHistorical}/{resultIndex}/{onlyQueryCustomResultIndex}', adService.getAnomalyResults ); apiRouter.get( - '/detectors/{id}/results/{isHistorical}', + '/detectors/{id}/{dataSourceId}/results/{isHistorical}', adService.getAnomalyResults ); - apiRouter.delete('/detectors/{detectorId}', adService.deleteDetector); - apiRouter.post('/detectors/{detectorId}/start', adService.startDetector); + apiRouter.delete('/detectors/{detectorId}/{dataSourceId}', adService.deleteDetector); + apiRouter.post('/detectors/{detectorId}/{dataSourceId}/start', adService.startDetector); apiRouter.post( - '/detectors/{detectorId}/stop/{isHistorical}', + '/detectors/{detectorId}/{dataSourceId}/stop/{isHistorical}', adService.stopDetector ); apiRouter.get( '/detectors/{detectorId}/_profile', adService.getDetectorProfile ); + apiRouter.get('/detectors/{detectorId}/{dataSourceId}', adService.getDetector); + apiRouter.get('/detectors/{detectorName}/_match', adService.matchDetector); apiRouter.get('/detectors/_count', adService.getDetectorCount); apiRouter.post( - '/detectors/{detectorId}/_topAnomalies/{isHistorical}', + '/detectors/{detectorId}/{dataSourceId}/_topAnomalies/{isHistorical}', adService.getTopAnomalyResults ); apiRouter.post( @@ -119,11 +119,12 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; + const { dataSourceId } = request.params as { dataSourceId: string }; const callWithRequest = getClientBasedOnDataSource( context, this.dataSourceEnabled, request, - '4585f560-d1ef-11ee-aa63-2181676cc573', + dataSourceId, this.client); const response = await callWithRequest( @@ -131,12 +132,6 @@ export default class AdService { detectorId, }); - - // const response = await this.client - // .asScoped(request) - // .callAsCurrentUser('ad.deleteDetector', { - // detectorId, - // }); return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -163,24 +158,11 @@ export default class AdService { const requestBody = JSON.stringify( convertPreviewInputKeysToSnakeCase(request.body) ); - - const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, - '4585f560-d1ef-11ee-aa63-2181676cc573', - this.client); - - const response = await callWithRequest( - 'ad.previewDetector', { + const response = await this.client + .asScoped(request) + .callAsCurrentUser('ad.previewDetector', { body: requestBody, }); - - // const response = await this.client - // .asScoped(request) - // .callAsCurrentUser('ad.previewDetector', { - // body: requestBody, - // }); const transformedKeys = mapKeysDeep(response, toCamel); return opensearchDashboardsResponse.ok({ body: { @@ -207,7 +189,6 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; - const { dataSourceId } = request.params as { dataSourceId: string }; //@ts-ignore const ifSeqNo = request.body.seqNo; //@ts-ignore @@ -224,31 +205,16 @@ export default class AdService { }; let response; - const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, - dataSourceId, - this.client); - if (isNumber(ifSeqNo) && isNumber(ifPrimaryTerm)) { - response = await callWithRequest( - 'ad.updateDetector', params); - - // response = await this.client - // .asScoped(request) - // .callAsCurrentUser('ad.updateDetector', params); + response = await this.client + .asScoped(request) + .callAsCurrentUser('ad.updateDetector', params); } else { - response = await callWithRequest( - 'ad.createDetector', { - body: requestBody, + response = await this.client + .asScoped(request) + .callAsCurrentUser('ad.createDetector', { + body: params.body, }); - - // response = await this.client - // .asScoped(request) - // .callAsCurrentUser('ad.createDetector', { - // body: params.body, - // }); } const resp = { ...response.anomaly_detector, @@ -285,25 +251,12 @@ export default class AdService { const requestBody = JSON.stringify( convertPreviewInputKeysToSnakeCase(request.body) ); - const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, - '4585f560-d1ef-11ee-aa63-2181676cc573', - this.client); - - const response = await callWithRequest( - 'ad.validateDetector', { + const response = await this.client + .asScoped(request) + .callAsCurrentUser('ad.validateDetector', { body: requestBody, validationType: validationType, }); - - // const response = await this.client - // .asScoped(request) - // .callAsCurrentUser('ad.validateDetector', { - // body: requestBody, - // validationType: validationType, - // }); return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -328,13 +281,12 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; - const { dataSourceId = '' } = request.query as MDSQueryParams; - console.log("getDetector: " + dataSourceId); + const { dataSourceId } = request.params as { dataSourceId: string }; const callWithRequest = getClientBasedOnDataSource( context, this.dataSourceEnabled, request, - '4585f560-d1ef-11ee-aa63-2181676cc573', + dataSourceId, this.client); const detectorResponse = await callWithRequest( @@ -361,7 +313,7 @@ export default class AdService { context, this.dataSourceEnabled, request, - "4585f560-d1ef-11ee-aa63-2181676cc573", + dataSourceId, this.client); realtimeTasksResponse = await callWithRequest( @@ -437,6 +389,7 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; + const { dataSourceId } = request.params as { dataSourceId: string }; //@ts-ignore const startTime = request.body?.startTime; //@ts-ignore @@ -459,14 +412,11 @@ export default class AdService { context, this.dataSourceEnabled, request, - '4585f560-d1ef-11ee-aa63-2181676cc573', + dataSourceId, this.client); const response = await callWithRequest(requestPath, requestParams); - // const response = await this.client - // .asScoped(request) - // .callAsCurrentUser(requestPath, requestParams); return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -490,10 +440,10 @@ export default class AdService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { try { - let { detectorId, isHistorical, dataSourceId } = request.params as { + let { detectorId, dataSourceId, isHistorical } = request.params as { detectorId: string; - isHistorical: any; dataSourceId: string; + isHistorical: any; }; isHistorical = JSON.parse(isHistorical) as boolean; const requestPath = isHistorical @@ -536,25 +486,11 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; - - - const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, - '4585f560-d1ef-11ee-aa63-2181676cc573', - this.client); - - const response = await callWithRequest( - 'ad.detectorProfile', { + const response = await this.client + .asScoped(request) + .callAsCurrentUser('ad.detectorProfile', { detectorId, }); - - // const response = await this.client - // .asScoped(request) - // .callAsCurrentUser('ad.detectorProfile', { - // detectorId, - // }); return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -579,20 +515,9 @@ export default class AdService { ): Promise> => { try { const requestBody = JSON.stringify(request.body); - - const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, - '4585f560-d1ef-11ee-aa63-2181676cc573', - this.client); - - const response: SearchResponse = await callWithRequest( - 'ad.searchDetector', { body: requestBody }); - - // const response: SearchResponse = await this.client - // .asScoped(request) - // .callAsCurrentUser('ad.searchDetector', { body: requestBody }); + const response: SearchResponse = await this.client + .asScoped(request) + .callAsCurrentUser('ad.searchDetector', { body: requestBody }); const totalDetectors = get(response, 'hits.total.value', 0); const detectors = get(response, 'hits.hits', []).map((detector: any) => ({ ...convertDetectorKeysToCamelCase(detector._source), @@ -631,8 +556,9 @@ export default class AdService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { try { - var { resultIndex, onlyQueryCustomResultIndex } = request.params as { + var { resultIndex, dataSourceId, onlyQueryCustomResultIndex } = request.params as { resultIndex: string; + dataSourceId: string; onlyQueryCustomResultIndex: boolean; }; if ( @@ -652,32 +578,17 @@ export default class AdService { context, this.dataSourceEnabled, request, - '4585f560-d1ef-11ee-aa63-2181676cc573', + dataSourceId, this.client); const response = !resultIndex - ? await callWithRequest( - 'ad.searchResults', { - body: requestBody, - }) - : await callWithRequest( - 'ad.searchResultsFromCustomResultIndex', { - ...requestParams, - body: requestBody, - }); - - // const response = !resultIndex - // ? await this.client - // .asScoped(request) - // .callAsCurrentUser('ad.searchResults', { - // body: requestBody, - // }) - // : await this.client - // .asScoped(request) - // .callAsCurrentUser('ad.searchResultsFromCustomResultIndex', { - // ...requestParams, - // body: requestBody, - // }); + ? await callWithRequest('ad.searchResults', { + body: requestBody, + }) + : await callWithRequest('ad.searchResultsFromCustomResultIndex', { + ...requestParams, + body: requestBody, + }); return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -713,7 +624,7 @@ export default class AdService { indices = '', sortDirection = SORT_DIRECTION.DESC, sortField = 'name', - dataSourceId = '', + dataSourceId = '' } = request.query as GetDetectorsQueryParams; const mustQueries = []; if (search.trim()) { @@ -756,16 +667,15 @@ export default class AdService { }, }, }; + const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client - ); - const response = await callWithRequest('ad.searchDetector', { - body: requestBody, - }); + this.client); + const response = await callWithRequest('ad.searchDetector', { body: requestBody }); + const totalDetectors = get(response, 'hits.total.value', 0); //Get all detectors from search detector API @@ -796,9 +706,9 @@ export default class AdService { resultIndex: CUSTOM_AD_RESULT_INDEX_PREFIX + '*', onlyQueryCustomResultIndex: 'false', } as {}; + const aggregationResult = await callWithRequest( - 'ad.searchResultsFromCustomResultIndex', - { + 'ad.searchResultsFromCustomResultIndex', { ...requestParams, body: getResultAggregationQuery(allDetectorIds, { from, @@ -808,8 +718,7 @@ export default class AdService { search, indices, }), - } - ); + }); const aggsDetectors = get( aggregationResult, 'aggregations.unique_detectors.buckets', @@ -862,12 +771,15 @@ export default class AdService { let realtimeTasksResponse = {} as any; let historicalTasksResponse = {} as any; try { - realtimeTasksResponse = await callWithRequest('ad.searchTasks', { - body: getLatestDetectorTasksQuery(true), + realtimeTasksResponse = await callWithRequest( + 'ad.searchTasks', { + body: getLatestDetectorTasksQuery(true), }); - historicalTasksResponse = await callWithRequest('ad.searchTasks', { + historicalTasksResponse = await callWithRequest( + 'ad.searchTasks', { body: getLatestDetectorTasksQuery(false), }); + } catch (err) { if (!isIndexNotFoundError(err)) { throw err; @@ -955,7 +867,6 @@ export default class AdService { resultIndex: string; onlyQueryCustomResultIndex: boolean; }; - console.log("getAnomalyResults: " + dataSourceId); if ( !resultIndex || !resultIndex.startsWith(CUSTOM_AD_RESULT_INDEX_PREFIX) @@ -1104,7 +1015,7 @@ export default class AdService { ...requestParams, body: requestBody, }); - + const totalResults: number = get(response, 'hits.total.value', 0); const detectorResult: AnomalyResult[] = []; @@ -1188,8 +1099,9 @@ export default class AdService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { try { - let { detectorId, isHistorical } = request.params as { + let { detectorId, dataSourceId, isHistorical } = request.params as { detectorId: string; + dataSourceId: string; isHistorical: any; }; isHistorical = JSON.parse(isHistorical) as boolean; @@ -1202,7 +1114,7 @@ export default class AdService { context, this.dataSourceEnabled, request, - '4585f560-d1ef-11ee-aa63-2181676cc573', + dataSourceId, this.client); const response = await callWithRequest( @@ -1211,13 +1123,6 @@ export default class AdService { body: request.body, }); - // const response = await this.client - // .asScoped(request) - // .callAsCurrentUser(requestPath, { - // detectorId: detectorId, - // body: request.body, - // }); - return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -1242,24 +1147,11 @@ export default class AdService { ): Promise> => { try { const { detectorName } = request.params as { detectorName: string }; - - const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, - '4585f560-d1ef-11ee-aa63-2181676cc573', - this.client); - - const response = await callWithRequest( - 'ad.matchDetector', { - detectorName + const response = await this.client + .asScoped(request) + .callAsCurrentUser('ad.matchDetector', { + detectorName, }); - - // const response = await this.client - // .asScoped(request) - // .callAsCurrentUser('ad.matchDetector', { - // detectorName, - // }); return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -1280,18 +1172,9 @@ export default class AdService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { try { - const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, - '4585f560-d1ef-11ee-aa63-2181676cc573', - this.client); - - const response = await callWithRequest('ad.detectorCount'); - - // const response = await this.client - // .asScoped(request) - // .callAsCurrentUser('ad.detectorCount'); + const response = await this.client + .asScoped(request) + .callAsCurrentUser('ad.detectorCount'); return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -1341,4 +1224,4 @@ export default class AdService { } return expectedValue; }; -} +} \ No newline at end of file From 2bb2a895ac6f608233901c63053821bfb47c1465 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Tue, 9 Apr 2024 14:55:45 -0700 Subject: [PATCH 11/26] version change Signed-off-by: Jackie Han --- opensearch_dashboards.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index d2e91e3c..9abd7894 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -1,7 +1,7 @@ { "id": "anomalyDetectionDashboards", "version": "2.11.0.0", - "opensearchDashboardsVersion": "2.11:.0", + "opensearchDashboardsVersion": "2.11.0", "configPath": [ "anomaly_detection_dashboards" ], From c363632bfd2aff52e5c37b6f90e656658091080b Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Tue, 9 Apr 2024 15:36:21 -0700 Subject: [PATCH 12/26] list detector list change Signed-off-by: Jackie Han --- public/redux/reducers/ad.ts | 2 +- server/routes/ad.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/redux/reducers/ad.ts b/public/redux/reducers/ad.ts index 57664ee6..30202d7f 100644 --- a/public/redux/reducers/ad.ts +++ b/public/redux/reducers/ad.ts @@ -402,7 +402,7 @@ export const getDetectorList = (queryParams: GetDetectorsQueryParams): APIAction return { type: GET_DETECTOR_LIST, request: (client: HttpSetup) => - client.get(`..${AD_NODE_API.DETECTOR}`, { query: queryParams }), + client.get(`..${AD_NODE_API.DETECTOR}/${queryParams.dataSourceId}}`, { query: queryParams }), }; }; diff --git a/server/routes/ad.ts b/server/routes/ad.ts index e13d163b..1436f394 100644 --- a/server/routes/ad.ts +++ b/server/routes/ad.ts @@ -69,7 +69,7 @@ export function registerADRoutes(apiRouter: Router, adService: AdService) { '/detectors/results/{dataSourceId}/_search/{resultIndex}/{onlyQueryCustomResultIndex}', adService.searchResults ); - apiRouter.get('/detectors', adService.getDetectors); + apiRouter.get('/detectors/{dataSourceId}', adService.getDetectors); apiRouter.post('/detectors/preview', adService.previewDetector); apiRouter.get( '/detectors/{id}/{dataSourceId}/results/{isHistorical}/{resultIndex}/{onlyQueryCustomResultIndex}', From c2d97dc32074db5ee676ca910076007a88ba4325 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Fri, 12 Apr 2024 10:27:58 -0700 Subject: [PATCH 13/26] Support MDS on List, Detail, Dashboard, Overview pages Signed-off-by: Jackie Han --- public/anomaly_detection_app.tsx | 3 - .../containers/AnomaliesChart.tsx | 3 + .../containers/AnomalyDetailsChart.tsx | 7 +- .../containers/AnomalyOccurrenceChart.tsx | 2 + .../hooks/useFetchDetectorInfo.ts | 5 +- .../Components/AnomaliesDistribution.tsx | 2 + .../Components/AnomaliesLiveChart.tsx | 7 +- .../Dashboard/Container/DashboardOverview.tsx | 46 +++--- public/pages/Dashboard/utils/utils.tsx | 14 +- .../containers/DetectorDetail.tsx | 45 +++--- .../hooks/useFetchMonitorInfo.ts | 11 +- .../containers/AnomalyHistory.tsx | 2 + .../containers/AnomalyResults.tsx | 9 +- .../containers/AnomalyResultsLiveChart.tsx | 8 +- .../DetectorsList/containers/List/List.tsx | 72 +++++---- .../pages/DetectorsList/utils/tableUtils.tsx | 38 +++-- .../containers/AnomalyDetectionOverview.tsx | 118 ++++++++------- public/pages/main/Main.tsx | 43 ++---- public/pages/utils/anomalyResultUtils.ts | 2 + public/plugin.ts | 4 +- public/redux/reducers/ad.ts | 129 +++++++++------- public/redux/reducers/alerting.ts | 13 +- public/redux/reducers/anomalyResults.ts | 102 ++++++------- public/redux/reducers/liveAnomalyResults.ts | 38 ++--- public/redux/reducers/opensearch.ts | 68 +++++---- public/redux/reducers/sampleData.ts | 16 +- public/services.ts | 4 + server/plugin.ts | 4 +- server/routes/ad.ts | 140 ++++++++++++------ server/routes/alerting.ts | 21 ++- server/routes/opensearch.ts | 57 ++++--- server/routes/sampleData.ts | 10 +- server/sampleData/utils/helpers.ts | 17 ++- 33 files changed, 603 insertions(+), 457 deletions(-) diff --git a/public/anomaly_detection_app.tsx b/public/anomaly_detection_app.tsx index 87b11cfb..25546899 100644 --- a/public/anomaly_detection_app.tsx +++ b/public/anomaly_detection_app.tsx @@ -17,13 +17,11 @@ import { Main } from './pages/main'; import { Provider } from 'react-redux'; import configureStore from './redux/configureStore'; import { CoreServicesContext } from './components/CoreServices/CoreServices'; -import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public'; import { DataSourcePluginSetup } from '../../../src/plugins/data_source/public'; export function renderApp( coreStart: CoreStart, params: AppMountParameters, - dataSourceManagement: DataSourceManagementPluginSetup, dataSource: DataSourcePluginSetup ) { const http = coreStart.http; @@ -45,7 +43,6 @@ export function renderApp(
    diff --git a/public/pages/AnomalyCharts/containers/AnomaliesChart.tsx b/public/pages/AnomalyCharts/containers/AnomaliesChart.tsx index 49f97213..67d0a004 100644 --- a/public/pages/AnomalyCharts/containers/AnomaliesChart.tsx +++ b/public/pages/AnomalyCharts/containers/AnomaliesChart.tsx @@ -85,6 +85,7 @@ export interface AnomaliesChartProps { selectedCategoryFields?: any[]; handleCategoryFieldsChange(selectedOptions: any[]): void; openOutOfRangeCallOut?: boolean; + dataSourceId?: string; } export const AnomaliesChart = React.memo((props: AnomaliesChartProps) => { @@ -345,6 +346,7 @@ export const AnomaliesChart = React.memo((props: AnomaliesChartProps) => { isHCDetector={props.isHCDetector} isHistorical={props.isHistorical} selectedHeatmapCell={props.selectedHeatmapCell} + dataSourceId={props.dataSourceId} />, , { isHistorical={props.isHistorical} onDatePickerRangeChange={handleDatePickerRangeChange} openOutOfRangeCallOut={showOutOfRangeCallOut} + dataSourceId={props.dataSourceId} /> )} diff --git a/public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx b/public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx index e2116e30..8ee7b37b 100644 --- a/public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx +++ b/public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx @@ -113,6 +113,7 @@ interface AnomalyDetailsChartProps { selectedHeatmapCell?: HeatmapCell; onDatePickerRangeChange?(startDate: number, endDate: number): void; openOutOfRangeCallOut?: boolean; + dataSourceId?: string; } export const AnomalyDetailsChart = React.memo( @@ -174,7 +175,7 @@ export const AnomalyDetailsChart = React.memo( zoomRange.endDate, taskId ); - dispatch(searchResults(anomalyDataRangeQuery, resultIndex, true)) + dispatch(searchResults(anomalyDataRangeQuery, resultIndex, props.dataSourceId, true)) .then((response: any) => { // Only retrieve buckets that are in the anomaly results range. This is so // we don't show aggregate results for where there is no data at all @@ -193,7 +194,7 @@ export const AnomalyDetailsChart = React.memo( taskId, selectedAggId ); - dispatch(searchResults(historicalAggQuery, resultIndex, true)) + dispatch(searchResults(historicalAggQuery, resultIndex, props.dataSourceId, true)) .then((response: any) => { const aggregatedAnomalies = parseHistoricalAggregatedAnomalies( response, @@ -229,7 +230,7 @@ export const AnomalyDetailsChart = React.memo( zoomRange.endDate, taskId ); - dispatch(searchResults(anomalyDataRangeQuery, resultIndex, true)) + dispatch(searchResults(anomalyDataRangeQuery, resultIndex, props.dataSourceId, true)) .then((response: any) => { const dataStartDate = get( response, diff --git a/public/pages/AnomalyCharts/containers/AnomalyOccurrenceChart.tsx b/public/pages/AnomalyCharts/containers/AnomalyOccurrenceChart.tsx index 0bb8d9df..92823f23 100644 --- a/public/pages/AnomalyCharts/containers/AnomalyOccurrenceChart.tsx +++ b/public/pages/AnomalyCharts/containers/AnomalyOccurrenceChart.tsx @@ -44,6 +44,7 @@ interface AnomalyOccurrenceChartProps { isHCDetector?: boolean; isHistorical?: boolean; selectedHeatmapCell?: HeatmapCell; + dataSourceId?: string; } export const AnomalyOccurrenceChart = React.memo( @@ -82,6 +83,7 @@ export const AnomalyOccurrenceChart = React.memo( isHCDetector={props.isHCDetector} isHistorical={props.isHistorical} selectedHeatmapCell={props.selectedHeatmapCell} + dataSourceId={props.dataSourceId} /> {props.isHCDetector && props.selectedHeatmapCell === undefined ? ( diff --git a/public/pages/CreateDetectorSteps/hooks/useFetchDetectorInfo.ts b/public/pages/CreateDetectorSteps/hooks/useFetchDetectorInfo.ts index 2aaa4f37..3f8d024d 100644 --- a/public/pages/CreateDetectorSteps/hooks/useFetchDetectorInfo.ts +++ b/public/pages/CreateDetectorSteps/hooks/useFetchDetectorInfo.ts @@ -21,7 +21,8 @@ import { getMappings } from '../../../redux/reducers/opensearch'; // 1. Get detector // 2. Gets index mapping export const useFetchDetectorInfo = ( - detectorId: string + detectorId: string, + dataSourceId: string ): { detector: Detector; hasError: boolean; @@ -44,7 +45,7 @@ export const useFetchDetectorInfo = ( const fetchDetector = async () => { if (!detector) { // hardcoding the datasource id for now, will update it later when working on create page - await dispatch(getDetector(detectorId, '4585f560-d1ef-11ee-aa63-2181676cc573')); + await dispatch(getDetector(detectorId, dataSourceId)); } if (selectedIndices) { await dispatch(getMappings(selectedIndices)); diff --git a/public/pages/Dashboard/Components/AnomaliesDistribution.tsx b/public/pages/Dashboard/Components/AnomaliesDistribution.tsx index b02878fe..06b27eab 100644 --- a/public/pages/Dashboard/Components/AnomaliesDistribution.tsx +++ b/public/pages/Dashboard/Components/AnomaliesDistribution.tsx @@ -35,6 +35,7 @@ import { ALL_CUSTOM_AD_RESULT_INDICES } from '../../utils/constants'; import { searchResults } from '../../../redux/reducers/anomalyResults'; export interface AnomaliesDistributionChartProps { selectedDetectors: DetectorListItem[]; + dataSourceId?: string; } export const AnomaliesDistributionChart = ( @@ -66,6 +67,7 @@ export const AnomaliesDistributionChart = ( await getAnomalyDistributionForDetectorsByTimeRange( searchResults, props.selectedDetectors, + props.dataSourceId, timeRange, dispatch, 0, diff --git a/public/pages/Dashboard/Components/AnomaliesLiveChart.tsx b/public/pages/Dashboard/Components/AnomaliesLiveChart.tsx index a0176b22..005c0773 100644 --- a/public/pages/Dashboard/Components/AnomaliesLiveChart.tsx +++ b/public/pages/Dashboard/Components/AnomaliesLiveChart.tsx @@ -57,6 +57,7 @@ import { searchResults } from '../../../redux/reducers/anomalyResults'; export interface AnomaliesLiveChartProps { selectedDetectors: DetectorListItem[]; + dataSourceId?: string; } interface LiveTimeRangeState { @@ -102,7 +103,8 @@ export const AnomaliesLiveChart = (props: AnomaliesLiveChartProps) => { 1, true, ALL_CUSTOM_AD_RESULT_INDICES, - false + false, + props.dataSourceId ); } catch (err) { console.log( @@ -126,7 +128,8 @@ export const AnomaliesLiveChart = (props: AnomaliesLiveChartProps) => { MAX_LIVE_DETECTORS, false, ALL_CUSTOM_AD_RESULT_INDICES, - false + false, + props.dataSourceId ); setLiveAnomalyData(latestLiveAnomalyResult); diff --git a/public/pages/Dashboard/Container/DashboardOverview.tsx b/public/pages/Dashboard/Container/DashboardOverview.tsx index 9c8c3b9e..647b8ad7 100644 --- a/public/pages/Dashboard/Container/DashboardOverview.tsx +++ b/public/pages/Dashboard/Container/DashboardOverview.tsx @@ -49,16 +49,12 @@ import { } from '../../../../server/utils/helpers'; import { CoreServicesContext } from '../../../components/CoreServices/CoreServices'; import { CoreStart, MountPoint } from '../../../../../../src/core/public'; -import { DataSourceManagementPluginSetup, DataSourceSelectableConfig } from '../../../../../../src/plugins/data_source_management/public'; -import { getNotifications, getSavedObjectsClient } from '../../../services'; +import { DataSourceSelectableConfig } from '../../../../../../src/plugins/data_source_management/public'; +import { getDataSourceManagementPlugin, getNotifications, getSavedObjectsClient } from '../../../services'; import { RouteComponentProps } from 'react-router-dom'; -export interface DashboardOverviewRouterParams { - dataSourceId: string; -} - -interface OverviewProps extends RouteComponentProps { - dataSourceManagement: DataSourceManagementPluginSetup; +interface OverviewProps extends RouteComponentProps { + dataSourceEnabled: boolean; setActionMenu: (menuMount: MountPoint | undefined) => void; } @@ -132,12 +128,18 @@ export function DashboardOverview(props: OverviewProps) { setAllDetectorStatesSelected(isEmpty(selectedStates)); }; - const handleDataSourceChange = (e) => { - const dataConnectionId = e[0] ? e[0].id : undefined; - setMDSOverviewState({ - queryParams: dataConnectionId, - selectedDataSourceId: dataConnectionId, - }); + const handleDataSourceChange = ([event]) => { + const dataSourceId = event?.id; + if (!dataSourceId) { + getNotifications().toasts.addDanger( + prettifyErrorMessage('Unable to set data source.') + ); + } else { + setMDSOverviewState({ + queryParams: dataSourceId, + selectedDataSourceId: dataSourceId, + }); + } } const opensearchState = useSelector((state: AppState) => state.opensearch); @@ -190,8 +192,8 @@ export function DashboardOverview(props: OverviewProps) { const intializeDetectors = async () => { dispatch(getDetectorList(getAllDetectorsQueryParamsWithDataSourceId(MDSOverviewState.selectedDataSourceId))); - dispatch(getIndices('')); - dispatch(getAliases('')); + dispatch(getIndices('', MDSOverviewState.selectedDataSourceId)); + dispatch(getAliases('', MDSOverviewState.selectedDataSourceId)); }; useEffect(() => { @@ -203,7 +205,9 @@ export function DashboardOverview(props: OverviewProps) { ...location, search: queryString.stringify(updatedParams), }) - intializeDetectors(); + if (props.dataSourceEnabled ? MDSOverviewState.selectedDataSourceId : true) { + intializeDetectors(); + } }, [MDSOverviewState]); useEffect(() => { @@ -238,7 +242,7 @@ export function DashboardOverview(props: OverviewProps) { }, [selectedDetectorsName, selectedIndices, selectedDetectorStates]); const DataSourceMenu = - props.dataSourceManagement.ui.getDataSourceMenu(); + getDataSourceManagementPlugin().ui.getDataSourceMenu(); const renderDataSourceComponent = useMemo(() => { return ( @@ -307,12 +312,15 @@ export function DashboardOverview(props: OverviewProps) { - + diff --git a/public/pages/Dashboard/utils/utils.tsx b/public/pages/Dashboard/utils/utils.tsx index 451d2b90..188cbab9 100644 --- a/public/pages/Dashboard/utils/utils.tsx +++ b/public/pages/Dashboard/utils/utils.tsx @@ -433,6 +433,7 @@ export const getLatestAnomalyResultsByTimeRange = async ( func: ( request: any, resultIndex: string, + dataSourceId: string, onlyQueryCustomResultIndex: boolean ) => APIAction, timeRange: string, @@ -441,7 +442,8 @@ export const getLatestAnomalyResultsByTimeRange = async ( anomalySize: number, checkLastIndexOnly: boolean, resultIndex: string, - onlyQueryCustomResultIndex: boolean + onlyQueryCustomResultIndex: boolean, + dataSourceId = '' ): Promise => { let from = 0; let anomalyResults = [] as object[]; @@ -457,6 +459,7 @@ export const getLatestAnomalyResultsByTimeRange = async ( checkLastIndexOnly ), resultIndex, + dataSourceId, onlyQueryCustomResultIndex ) ); @@ -489,6 +492,7 @@ export const getLatestAnomalyResultsForDetectorsByTimeRange = async ( func: ( request: any, resultIndex: string, + dataSourceId: string, onlyQueryCustomResultIndex: boolean ) => APIAction, selectedDetectors: DetectorListItem[], @@ -499,7 +503,8 @@ export const getLatestAnomalyResultsForDetectorsByTimeRange = async ( detectorNum: number, checkLastIndexOnly: boolean, resultIndex: string, - onlyQueryCustomResultIndex: boolean + onlyQueryCustomResultIndex: boolean, + dataSourceId = '' ): Promise => { const detectorAndIdMap = buildDetectorAndIdMap(selectedDetectors); let from = 0; @@ -516,6 +521,7 @@ export const getLatestAnomalyResultsForDetectorsByTimeRange = async ( checkLastIndexOnly ), resultIndex, + dataSourceId, onlyQueryCustomResultIndex ) ); @@ -605,9 +611,11 @@ export const getAnomalyDistributionForDetectorsByTimeRange = async ( func: ( request: any, resultIndex: string, + dataSourceId: string, onlyQueryCustomResultIndex: boolean ) => APIAction, selectedDetectors: DetectorListItem[], + dataSourceId = '', timeRange: string, dispatch: Dispatch, threshold: number, @@ -638,7 +646,7 @@ export const getAnomalyDistributionForDetectorsByTimeRange = async ( const finalQuery = Object.assign({}, getResultQuery, anomaly_dist_aggs); const result = await dispatch( - func(finalQuery, resultIndex, onlyQueryCustomResultIndex) + func(finalQuery, resultIndex, dataSourceId, onlyQueryCustomResultIndex) ); const detectorsAggResults = get( diff --git a/public/pages/DetectorDetail/containers/DetectorDetail.tsx b/public/pages/DetectorDetail/containers/DetectorDetail.tsx index c3663eb7..715e7151 100644 --- a/public/pages/DetectorDetail/containers/DetectorDetail.tsx +++ b/public/pages/DetectorDetail/containers/DetectorDetail.tsx @@ -1,3 +1,4 @@ + /* * SPDX-License-Identifier: Apache-2.0 * @@ -58,14 +59,14 @@ import { import { DETECTOR_STATE } from '../../../../server/utils/constants'; import { CatIndex } from '../../../../server/models/types'; import { containsIndex } from '../utils/helpers'; -import { DataSourceManagementPluginSetup, DataSourceViewConfig } from '../../../../../../src/plugins/data_source_management/public'; +import { DataSourceViewConfig } from '../../../../../../src/plugins/data_source_management/public'; +import { getDataSourceManagementPlugin, getNotifications, getSavedObjectsClient } from '../../../services'; export interface DetectorRouterProps { detectorId?: string; } interface DetectorDetailProps extends RouteComponentProps { dataSourceEnabled: boolean; - dataSourceManagement: DataSourceManagementPluginSetup; setActionMenu: (menuMount: MountPoint | undefined) => void; } @@ -104,7 +105,6 @@ interface DetectorDetailModel { } export const DetectorDetail = (props: DetectorDetailProps) => { - console.log(props); const core = React.useContext(CoreServicesContext) as CoreStart; const dispatch = useDispatch(); const detectorId = get(props, 'match.params.detectorId', '') as string; @@ -114,9 +114,8 @@ export const DetectorDetail = (props: DetectorDetailProps) => { const dataSourceId = queryParams.get('dataSourceId') as string; const { detector, hasError, isLoadingDetector, errorMessage } = - useFetchDetectorInfo(detectorId); - const { monitor, fetchMonitorError, isLoadingMonitor } = - useFetchMonitorInfo(detectorId); + useFetchDetectorInfo(detectorId, dataSourceId); + const { monitor} = useFetchMonitorInfo(detectorId, dataSourceId, props.dataSourceEnabled); const visibleIndices = useSelector( (state: AppState) => state.opensearch.indices ) as CatIndex[]; @@ -165,15 +164,17 @@ export const DetectorDetail = (props: DetectorDetailProps) => { // Getting all visible indices. Will re-fetch if changes to the detector (e.g., // detector starts, result index recreated or user switches tabs to re-fetch detector) useEffect(() => { - const getInitialIndices = async () => { - await dispatch(getIndices('', dataSourceId)).catch((error: any) => { - console.error(error); - core.notifications.toasts.addDanger('Error getting all indices'); - }); - }; - // only need to check if indices exist after detector finishes loading - if (!isLoadingDetector) { - getInitialIndices(); + if (props.dataSourceEnabled ? dataSourceId : true) { + const getInitialIndices = async () => { + await dispatch(getIndices('', dataSourceId)).catch((error: any) => { + console.error(error); + core.notifications.toasts.addDanger('Error getting all indices'); + }); + }; + // only need to check if indices exist after detector finishes loading + if (!isLoadingDetector) { + getInitialIndices(); + } } }, [detector]); @@ -255,9 +256,9 @@ export const DetectorDetail = (props: DetectorDetailProps) => { const listener: Listener = { onSuccess: () => { if (detectorDetailModel.showStopDetectorModalFor === 'detector') { - props.history.push(`/detectors/${detectorId}/edit?dataSourceId=${dataSourceId}`); + props.history.push(`/detectors/${detectorId}/edit`); } else { - props.history.push(`/detectors/${detectorId}/features?dataSourceId=${dataSourceId}`); + props.history.push(`/detectors/${detectorId}/features`); } hideStopDetectorModal(); }, @@ -360,7 +361,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { > ) : null; - const DataSourceMenu = props.dataSourceManagement.ui.getDataSourceMenu(); + const DataSourceMenu = getDataSourceManagementPlugin().ui.getDataSourceMenu(); return ( @@ -379,9 +380,10 @@ export const DetectorDetail = (props: DetectorDetailProps) => { setMenuMountPoint={props.setActionMenu} componentType={'DataSourceView'} componentConfig={{ - // give a placeholder label for now, will update it once neo team allows empty label field - activeOption: [{label: 'labelPlaceHolder', id: dataSourceId}], - fullWidth: true + activeOption: [{ id: dataSourceId}], + fullWidth: false, + savedObjects: getSavedObjectsClient(), + notifications: getNotifications(), }} /> )} @@ -567,6 +569,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { {...resultsProps} detectorId={detectorId} dataSourceId={dataSourceId} + dataSourceEnabled={props.dataSourceEnabled} onStartDetector={() => handleStartAdJob(detectorId)} onStopDetector={() => handleStopAdJob(detectorId)} onSwitchToConfiguration={handleSwitchToConfigurationTab} diff --git a/public/pages/DetectorDetail/hooks/useFetchMonitorInfo.ts b/public/pages/DetectorDetail/hooks/useFetchMonitorInfo.ts index a77d1b3c..5c90dbca 100644 --- a/public/pages/DetectorDetail/hooks/useFetchMonitorInfo.ts +++ b/public/pages/DetectorDetail/hooks/useFetchMonitorInfo.ts @@ -18,7 +18,9 @@ import { searchMonitors } from '../../../redux/reducers/alerting'; //A hook which gets AD monitor. export const useFetchMonitorInfo = ( - detectorId: string + detectorId: string, + dataSourceId = '', + dataSourceEnabled: boolean ): { monitor: Monitor | undefined; fetchMonitorError: boolean; @@ -26,10 +28,9 @@ export const useFetchMonitorInfo = ( } => { const dispatch = useDispatch(); useEffect(() => { - const fetchAdMonitors = async () => { - await dispatch(searchMonitors()); - }; - fetchAdMonitors(); + if (dataSourceEnabled ? dataSourceId : true) { + dispatch(searchMonitors(dataSourceId)); + } }, []); const isMonitorRequesting = useSelector( diff --git a/public/pages/DetectorResults/containers/AnomalyHistory.tsx b/public/pages/DetectorResults/containers/AnomalyHistory.tsx index 219270ec..6eaff6c4 100644 --- a/public/pages/DetectorResults/containers/AnomalyHistory.tsx +++ b/public/pages/DetectorResults/containers/AnomalyHistory.tsx @@ -876,6 +876,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { entityAnomalySummaries={entityAnomalySummaries} selectedCategoryFields={selectedCategoryFields} handleCategoryFieldsChange={handleCategoryFieldsChange} + dataSourceId={dataSourceId} >
    {isHCDetector @@ -919,6 +920,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { isHCDetector={isHCDetector} isHistorical={props.isHistorical} selectedHeatmapCell={selectedHeatmapCell} + dataSourceId={dataSourceId} />, , ] diff --git a/public/pages/DetectorResults/containers/AnomalyResults.tsx b/public/pages/DetectorResults/containers/AnomalyResults.tsx index f5a13d81..419d3a3e 100644 --- a/public/pages/DetectorResults/containers/AnomalyResults.tsx +++ b/public/pages/DetectorResults/containers/AnomalyResults.tsx @@ -71,6 +71,7 @@ import { DEFAULT_SHINGLE_SIZE } from '../../../utils/constants'; interface AnomalyResultsProps extends RouteComponentProps { detectorId: string; dataSourceId: string; + dataSourceEnabled: boolean; onStartDetector(): void; onStopDetector(): void; onSwitchToConfiguration(): void; @@ -92,11 +93,15 @@ export function AnomalyResults(props: AnomalyResultsProps) { BREADCRUMBS.DETECTORS, { text: detector ? detector.name : '' }, ]); - dispatch(getDetector(detectorId, dataSourceId)); + if (props.dataSourceEnabled ? dataSourceId : true) { + dispatch(getDetector(detectorId, dataSourceId)); + } }, []); const fetchDetector = async () => { - dispatch(getDetector(detectorId, dataSourceId)); + if (props.dataSourceEnabled ? dataSourceId : true) { + dispatch(getDetector(detectorId, dataSourceId)); + } }; useEffect(() => { diff --git a/public/pages/DetectorResults/containers/AnomalyResultsLiveChart.tsx b/public/pages/DetectorResults/containers/AnomalyResultsLiveChart.tsx index 5e38cece..1c331772 100644 --- a/public/pages/DetectorResults/containers/AnomalyResultsLiveChart.tsx +++ b/public/pages/DetectorResults/containers/AnomalyResultsLiveChart.tsx @@ -142,13 +142,6 @@ export const AnomalyResultsLiveChart = ( true ) ); - console.log(getDetectorLiveResults( - detectorId, - queryParams, - false, - resultIndex, - true - )); } catch (err) { console.error( `Failed to get live anomaly result for detector ${detectorId}`, @@ -171,6 +164,7 @@ export const AnomalyResultsLiveChart = ( getLiveAnomalyResults( dispatch, props.detector.id, + dataSourceId, detectionInterval, LIVE_CHART_CONFIG.MONITORING_INTERVALS, resultIndex, diff --git a/public/pages/DetectorsList/containers/List/List.tsx b/public/pages/DetectorsList/containers/List/List.tsx index d0e4a377..f1c1f058 100644 --- a/public/pages/DetectorsList/containers/List/List.tsx +++ b/public/pages/DetectorsList/containers/List/List.tsx @@ -83,11 +83,8 @@ import { } from '../../../../../server/utils/helpers'; import { CoreStart, MountPoint } from '../../../../../../../src/core/public'; import { CoreServicesContext } from '../../../../components/CoreServices/CoreServices'; -import { - DataSourceManagementPluginSetup, - DataSourceSelectableConfig, -} from '../../../../../../../src/plugins/data_source_management/public'; -import { getNotifications, getSavedObjectsClient } from '../../../../services'; +import { DataSourceSelectableConfig } from '../../../../../../../src/plugins/data_source_management/public'; +import { getDataSourceManagementPlugin, getNotifications, getSavedObjectsClient } from '../../../../services'; export interface ListRouterParams { from: string; @@ -100,7 +97,6 @@ export interface ListRouterParams { } interface ListProps extends RouteComponentProps { dataSourceEnabled: boolean; - dataSourceManagement: DataSourceManagementPluginSetup; setActionMenu: (menuMount: MountPoint | undefined) => void; } interface ListState { @@ -166,19 +162,12 @@ export const DetectorList = (props: ListProps) => { isStopDisabled: false, }); - // Getting all initial indices - const [indexQuery, setIndexQuery] = useState(''); - useEffect(() => { - const getInitialIndices = async () => { - await dispatch(getIndices(indexQuery)); - }; - getInitialIndices(); - }, []); - // Getting all initial monitors useEffect(() => { const getInitialMonitors = async () => { - dispatch(searchMonitors()); + if (props.dataSourceEnabled ? state.selectedDataSourceId : true) { + dispatch(searchMonitors(state.selectedDataSourceId)); + } }; getInitialMonitors(); }, []); @@ -225,6 +214,15 @@ export const DetectorList = (props: ListProps) => { ]); }, []); + // Getting all initial indices + const [indexQuery, setIndexQuery] = useState(''); + useEffect(() => { + const getInitialIndices = async () => { + await dispatch(getIndices(indexQuery, state.selectedDataSourceId)); + }; + getInitialIndices(); + }, [state.selectedDataSourceId]); + // Refresh data if user change any parameters / filter / sort useEffect(() => { const { history, location } = props; @@ -241,7 +239,10 @@ export const DetectorList = (props: ListProps) => { }); setIsLoadingFinalDetectors(true); - getUpdatedDetectors(); + + if (props.dataSourceEnabled ? state.selectedDataSourceId : true) { + getUpdatedDetectors(); + } }, [ state.page, state.queryParams, @@ -291,6 +292,7 @@ export const DetectorList = (props: ListProps) => { } } }, [confirmModalState.isRequestingToClose, isLoading]); + const getUpdatedDetectors = async () => { dispatch( getDetectorList( @@ -338,7 +340,7 @@ export const DetectorList = (props: ListProps) => { if (searchValue !== indexQuery) { const sanitizedQuery = sanitizeSearchText(searchValue); setIndexQuery(sanitizedQuery); - await dispatch(getPrioritizedIndices(sanitizedQuery)); + await dispatch(getPrioritizedIndices(sanitizedQuery, state.selectedDataSourceId)); setState((state) => ({ ...state, page: 0, @@ -478,7 +480,7 @@ export const DetectorList = (props: ListProps) => { DETECTOR_ACTION.START ).map((detector) => detector.id); const promises = validIds.map(async (id: string) => { - return dispatch(startDetector(id)); + return dispatch(startDetector(id, state.selectedDataSourceId)); }); await Promise.all(promises) .then(() => { @@ -494,7 +496,9 @@ export const DetectorList = (props: ListProps) => { ); }) .finally(() => { - getUpdatedDetectors(); + if (props.dataSourceEnabled ? state.selectedDataSourceId : true) { + getUpdatedDetectors(); + } }); }; @@ -505,7 +509,7 @@ export const DetectorList = (props: ListProps) => { DETECTOR_ACTION.STOP ).map((detector) => detector.id); const promises = validIds.map(async (id: string) => { - return dispatch(stopDetector(id)); + return dispatch(stopDetector(id, state.selectedDataSourceId)); }); await Promise.all(promises) .then(() => { @@ -537,7 +541,7 @@ export const DetectorList = (props: ListProps) => { DETECTOR_ACTION.DELETE ).map((detector) => detector.id); const promises = validIds.map(async (id: string) => { - return dispatch(deleteDetector(id)); + return dispatch(deleteDetector(id, state.selectedDataSourceId)); }); await Promise.all(promises) .then(() => { @@ -575,14 +579,19 @@ export const DetectorList = (props: ListProps) => { }); }; - const handleDataSourceChange = (e) => { - const dataConnectionId = e[0] ? e[0].id : undefined; - - setState({ - ...state, - page: 0, - selectedDataSourceId: dataConnectionId, - }); + const handleDataSourceChange = ([event]) => { + const dataSourceId = event?.id; + if (!dataSourceId) { + getNotifications().toasts.addDanger( + prettifyErrorMessage('Unable to set data source.') + ); + } else { + setState((prevState) => ({ + ...prevState, + page: 0, + selectedDataSourceId: dataSourceId, + })); + } }; const getConfirmModal = () => { @@ -660,7 +669,7 @@ export const DetectorList = (props: ListProps) => { const confirmModal = getConfirmModal(); const DataSourceMenu = - props.dataSourceManagement.ui.getDataSourceMenu(); + getDataSourceManagementPlugin().ui.getDataSourceMenu(); const renderDataSourceComponent = useMemo(() => { return ( { componentType={'DataSourceSelectable'} componentConfig={{ fullWidth: false, + activeOption:[{ id: state.selectedDataSourceId }], savedObjects: getSavedObjectsClient(), notifications: getNotifications(), onSelectedDataSources: (dataSources) => diff --git a/public/pages/DetectorsList/utils/tableUtils.tsx b/public/pages/DetectorsList/utils/tableUtils.tsx index ef14b815..f0ddf617 100644 --- a/public/pages/DetectorsList/utils/tableUtils.tsx +++ b/public/pages/DetectorsList/utils/tableUtils.tsx @@ -64,11 +64,17 @@ export const staticColumn = [ textOnly: true, align: 'left', width: '15%', - render: (name: string, detector: Detector) => ( - - {name} - - ), + render: (name: string, detector: Detector) => { + let href = `${PLUGIN_NAME}#/detectors/${detector.id}/results`; + if (detector.dataSourceId) { + href += `?dataSourceId=${detector.dataSourceId}`; + } + return ( + + {name} + + ); + }, }, { field: 'indices', @@ -111,13 +117,21 @@ export const staticColumn = [ align: 'left', width: '15%', render: (name: string, detector: Detector) => { - return !isEmpty(detector.taskId) ? ( - - View results - - ) : ( - - - ); + if (!isEmpty(detector.taskId)) { + let href = `${PLUGIN_NAME}#/detectors/${detector.id}/historical`; + if (detector.dataSourceId) { + href += `?dataSourceId=${detector.dataSourceId}`; + } + return ( + + View results + + ); + } else { + return ( + - + ); + } }, }, { diff --git a/public/pages/Overview/containers/AnomalyDetectionOverview.tsx b/public/pages/Overview/containers/AnomalyDetectionOverview.tsx index 80e40dd5..487dac19 100644 --- a/public/pages/Overview/containers/AnomalyDetectionOverview.tsx +++ b/public/pages/Overview/containers/AnomalyDetectionOverview.tsx @@ -17,12 +17,11 @@ import { EuiFlexItem, EuiFlexGroup, EuiLink, - EuiIcon, EuiButton, EuiLoadingSpinner, EuiFlexGrid, } from '@elastic/eui'; -import React, { Fragment, useEffect, useState } from 'react'; +import React, { Fragment, useEffect, useMemo, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { APP_PATH, @@ -32,7 +31,6 @@ import { } from '../../../utils/constants'; import { SAMPLE_TYPE } from '../../../../server/utils/constants'; import { - GET_SAMPLE_DETECTORS_QUERY_PARAMS, GET_SAMPLE_INDICES_QUERY, getSampleDetectorsQueryParamsWithDataSouceId, } from '../../utils/constants'; @@ -60,23 +58,18 @@ import { CoreServicesContext } from '../../../components/CoreServices/CoreServic import ContentPanel from '../../../components/ContentPanel/ContentPanel'; import { CreateWorkflowStepDetails } from '../components/CreateWorkflowStepDetails'; import { CreateWorkflowStepSeparator } from '../components/CreateWorkflowStepSeparator'; -import { DataSourceManagementPluginSetup, DataSourceSelectableConfig } from '../../../../../../src/plugins/data_source_management/public'; -import { getNotifications, getSavedObjectsClient } from '../../../../public/services'; +import { DataSourceSelectableConfig } from '../../../../../../src/plugins/data_source_management/public'; +import { getDataSourceManagementPlugin, getNotifications, getSavedObjectsClient } from '../../../../public/services'; import { MDSQueryParams } from 'server/models/types'; import { RouteComponentProps } from 'react-router-dom'; import queryString from 'querystring'; import { getURLQueryParams } from '../../../../public/pages/DetectorsList/utils/helpers'; -interface AnomalyDetectionOverviewProps extends RouteComponentProps { - isLoadingDetectors: boolean; - dataSourceManagement: DataSourceManagementPluginSetup; +interface AnomalyDetectionOverviewProps extends RouteComponentProps { + dataSourceEnabled: boolean; setActionMenu: (menuMount: MountPoint | undefined) => void; } -interface OverviewRouterParams { - dataSourceId: string; -} - interface MDSOverviewState { queryParams: MDSQueryParams; selectedDataSourceId: string; @@ -84,13 +77,13 @@ interface MDSOverviewState { export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { const core = React.useContext(CoreServicesContext) as CoreStart; + const isLoadingSampleDetectors = useSelector((state: AppState) => state.ad.requesting); + const isLoadingSampleIndices = useSelector((state: AppState) => state.opensearch.requesting); const dispatch = useDispatch(); const visibleSampleIndices = useSelector( (state: AppState) => state.opensearch.indices ); - console.log(props); - const allSampleDetectors = Object.values( useSelector((state: AppState) => state.ad.detectorList) ); @@ -113,20 +106,6 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { selectedDataSourceId: queryParams.dataSourceId ? queryParams.dataSourceId : '', }); - const getAllSampleDetectors = async () => { - await dispatch(getDetectorList(getSampleDetectorsQueryParamsWithDataSouceId(MDSOverviewState.selectedDataSourceId))).catch( - (error: any) => { - console.error('Error getting all detectors: ', error); - } - ); - }; - - const getAllSampleIndices = async () => { - await dispatch(getIndices(GET_SAMPLE_INDICES_QUERY)).catch((error: any) => { - console.error('Error getting all indices: ', error); - }); - }; - // Set breadcrumbs on page initialization useEffect(() => { core.chrome.setBreadcrumbs([BREADCRUMBS.ANOMALY_DETECTOR]); @@ -142,10 +121,25 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { ...location, search: queryString.stringify(updatedParams), }) - getAllSampleDetectors(); - getAllSampleIndices(); + + if (props.dataSourceEnabled ? MDSOverviewState.selectedDataSourceId : true) { + fetchData(); + } + }, [MDSOverviewState]); + // fetch smaple detectors and sample indices + const fetchData = async () => { + await dispatch(getDetectorList(getSampleDetectorsQueryParamsWithDataSouceId(MDSOverviewState.selectedDataSourceId))).catch( + (error: any) => { + console.error('Error getting sample detectors: ', error); + } + ); + await dispatch(getIndices(GET_SAMPLE_INDICES_QUERY, MDSOverviewState.selectedDataSourceId)).catch((error: any) => { + console.error('Error getting sample indices: ', error); + }); + }; + // Create and populate sample index, create and start sample detector const handleLoadData = async ( sampleType: SAMPLE_TYPE, @@ -159,7 +153,7 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { // Create the index (if it doesn't exist yet) if (!containsSampleIndex(visibleSampleIndices, sampleType)) { - await dispatch(createIndex(indexConfig)).catch((error: any) => { + await dispatch(createIndex(indexConfig, MDSOverviewState.selectedDataSourceId)).catch((error: any) => { errorDuringAction = true; errorMessage = 'Error creating sample index. ' + prettifyErrorMessage(error); @@ -169,7 +163,7 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { // Get the sample data from the server and bulk insert if (!errorDuringAction) { - await dispatch(createSampleData(sampleType)).catch((error: any) => { + await dispatch(createSampleData(sampleType, MDSOverviewState.selectedDataSourceId)).catch((error: any) => { errorDuringAction = true; errorMessage = prettifyErrorMessage(error.message); console.error('Error bulk inserting data: ', errorMessage); @@ -178,11 +172,11 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { // Create the detector if (!errorDuringAction) { - await dispatch(createDetector(detectorConfig)) + await dispatch(createDetector(detectorConfig, MDSOverviewState.selectedDataSourceId)) .then(function (response: any) { const detectorId = response.response.id; // Start the detector - dispatch(startDetector(detectorId)).catch((error: any) => { + dispatch(startDetector(detectorId, MDSOverviewState.selectedDataSourceId)).catch((error: any) => { errorDuringAction = true; errorMessage = prettifyErrorMessage(error.message); console.error('Error starting sample detector: ', errorMessage); @@ -194,9 +188,7 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { console.error('Error creating sample detector: ', errorMessage); }); } - - getAllSampleDetectors(); - getAllSampleIndices(); + fetchData(); setLoadingState(false); if (!errorDuringAction) { core.notifications.toasts.addSuccess( @@ -209,34 +201,48 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { } }; - const handleDataSourceChange = (e) => { - const dataConnectionId = e[0] ? e[0].id : undefined; - setMDSOverviewState({ - queryParams: dataConnectionId, - selectedDataSourceId: dataConnectionId, - }); + const handleDataSourceChange = ([event]) => { + const dataSourceId = event?.id; + + if (!dataSourceId) { + getNotifications().toasts.addDanger( + prettifyErrorMessage('Unable to set data source.') + ); + } else { + setMDSOverviewState({ + queryParams: dataSourceId, + selectedDataSourceId: dataSourceId, + }); + } } - const DataSourceMenu = props.dataSourceManagement.ui.getDataSourceMenu(); + const DataSourceMenu = + getDataSourceManagementPlugin().ui.getDataSourceMenu(); + const renderDataSourceComponent = useMemo(() => { + return ( + + handleDataSourceChange(dataSources), + }} + /> + ); + }, [getSavedObjectsClient, getNotifications, props.setActionMenu]); - return props.isLoadingDetectors ? ( + return isLoadingSampleDetectors && isLoadingSampleIndices ? (
    ) : ( - handleDataSourceChange(dataSources), - }} - /> + {props.dataSourceEnabled && renderDataSourceComponent} diff --git a/public/pages/main/Main.tsx b/public/pages/main/Main.tsx index d65beca4..22c67654 100644 --- a/public/pages/main/Main.tsx +++ b/public/pages/main/Main.tsx @@ -21,11 +21,10 @@ import { APP_PATH } from '../../utils/constants'; import { DetectorDetail } from '../DetectorDetail'; import { DefineDetector } from '../DefineDetector/containers/DefineDetector'; import { ConfigureModel } from '../ConfigureModel/containers/ConfigureModel'; -import { DashboardOverview, DashboardOverviewRouterParams } from '../Dashboard/Container/DashboardOverview'; +import { DashboardOverview } from '../Dashboard/Container/DashboardOverview'; import { CoreServicesConsumer } from '../../components/CoreServices/CoreServices'; import { CoreStart, MountPoint } from '../../../../../src/core/public'; import { AnomalyDetectionOverview } from '../Overview'; -import { DataSourceManagementPluginSetup } from '../../../../../src/plugins/data_source_management/public'; import { getURLQueryParams } from '../DetectorsList/utils/helpers'; enum Navigation { @@ -36,13 +35,11 @@ enum Navigation { interface MainProps extends RouteComponentProps { dataSourceEnabled: boolean; - dataSourceManagement: DataSourceManagementPluginSetup; setHeaderActionMenu: (menuMount: MountPoint | undefined) => void; } export function Main(props: MainProps) { - const { dataSourceEnabled, dataSourceManagement, setHeaderActionMenu } = - props; + const { dataSourceEnabled, setHeaderActionMenu } = props; const hideSideNavBar = useSelector( (state: AppState) => state.adApp.hideSideNavBar @@ -50,35 +47,29 @@ export function Main(props: MainProps) { const adState = useSelector((state: AppState) => state.ad); const totalDetectors = adState.totalDetectors; - const errorGettingDetectors = adState.errorMessage; - const isLoadingDetectors = adState.requesting; const queryParams = getURLQueryParams(props.location); const dataSourceId = queryParams.dataSourceId ? queryParams.dataSourceId : ''; - + const existingParams = "from=0&size=20&search=&indices=&sortField=name&sortDirection=asc"; const constructHrefWithDataSourceId = (basePath, existingParams, dataSourceId) => { - // Construct the full URL const fullUrl = `${window.location.origin}${basePath}?${existingParams}`; - //console.log("fullUrl: ", fullUrl); const url = new URL(fullUrl); - //console.log(url); - url.searchParams.set('dataSourceId', dataSourceId); // Append or update dataSourceId + url.searchParams.set('dataSourceId', dataSourceId); - // Return the constructed URL, excluding the origin part to match your structure return `#${url.pathname}${url.search}`; }; - - const existingParams = "from=0&size=20&search=&indices=&sortField=name&sortDirection=asc&dataSourceId=4585f560-d1ef-11ee-aa63-2181676cc573"; - + const dashboardHref = dataSourceId ? `#${APP_PATH.DASHBOARD}?dataSourceId=${dataSourceId}` : `#${APP_PATH.DASHBOARD}`; + const overviewHref = dataSourceId ? `#${APP_PATH.OVERVIEW}?dataSourceId=${dataSourceId}` : `#${APP_PATH.OVERVIEW}`; + const sideNav = [ { name: Navigation.AnomalyDetection, id: 0, - href: `#${APP_PATH.OVERVIEW}`, + href: overviewHref, items: [ { name: Navigation.Dashboard, id: 1, - href: `#${APP_PATH.DASHBOARD}?dataSourceId=${dataSourceId}`, + href: dashboardHref, isSelected: props.location.pathname === APP_PATH.DASHBOARD, }, { @@ -103,9 +94,9 @@ export function Main(props: MainProps) { ) => + render={(props: RouteComponentProps) => } @@ -116,7 +107,6 @@ export function Main(props: MainProps) { render={(props: RouteComponentProps) => ( @@ -148,7 +138,6 @@ export function Main(props: MainProps) { render={(props: RouteComponentProps) => ( )} @@ -164,8 +153,7 @@ export function Main(props: MainProps) { path={APP_PATH.OVERVIEW} render={(props: RouteComponentProps) => ( @@ -173,17 +161,16 @@ export function Main(props: MainProps) { /> ) => + render={(props: RouteComponentProps) => totalDetectors > 0 ? ( ) : ( diff --git a/public/pages/utils/anomalyResultUtils.ts b/public/pages/utils/anomalyResultUtils.ts index acb0b955..cc3408b6 100644 --- a/public/pages/utils/anomalyResultUtils.ts +++ b/public/pages/utils/anomalyResultUtils.ts @@ -96,6 +96,7 @@ export const getQueryParamsForLiveAnomalyResults = ( export const getLiveAnomalyResults = ( dispatch: Dispatch, detectorId: string, + dataSourceId: string, detectionInterval: number, intervals: number, resultIndex: string, @@ -108,6 +109,7 @@ export const getLiveAnomalyResults = ( dispatch( getDetectorLiveResults( detectorId, + dataSourceId, queryParams, false, resultIndex, diff --git a/public/plugin.ts b/public/plugin.ts index 97fc1eaa..c7d7572f 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -36,6 +36,7 @@ import { setUISettings, setQueryService, setSavedObjectsClient, + setDataSourceManagementPlugin, } from './services'; import { AnomalyDetectionOpenSearchDashboardsPluginStart } from 'public'; import { @@ -88,7 +89,6 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin return renderApp( coreStart, params, - plugins.dataSourceManagement, plugins.dataSource ); }, @@ -100,6 +100,8 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin // direct server-side calls setClient(core.http); + setDataSourceManagementPlugin(plugins.dataSourceManagement); + // Create context menu actions const actions = getActions(); diff --git a/public/redux/reducers/ad.ts b/public/redux/reducers/ad.ts index 30202d7f..4bb43e35 100644 --- a/public/redux/reducers/ad.ts +++ b/public/redux/reducers/ad.ts @@ -369,14 +369,17 @@ const reducer = handleActions( initialDetectorsState ); -export const createDetector = (requestBody: Detector): APIAction => ( - { - type: CREATE_DETECTOR, - request: (client: HttpSetup) => - client.post(`..${AD_NODE_API.DETECTOR}`, { +export const createDetector = (requestBody: Detector, dataSourceId = ''): APIAction => { + const url = dataSourceId ? `..${AD_NODE_API.DETECTOR}/${dataSourceId}` : `..${AD_NODE_API.DETECTOR}`; + + return { + type: CREATE_DETECTOR, + request: (client: HttpSetup) => + client.post(url, { body: JSON.stringify(requestBody), }), -}); + } +}; export const validateDetector = ( requestBody: Detector, @@ -389,20 +392,23 @@ export const validateDetector = ( }), }); -export const getDetector = (detectorId: string, dataSourceId: string): APIAction => ({ - type: GET_DETECTOR, - request: (client: HttpSetup) => - client.get(`..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}`), - detectorId, dataSourceId -}); +export const getDetector = (detectorId: string, dataSourceId = ''): APIAction => { + const baseUrl = `..${AD_NODE_API.DETECTOR}`; + const url = dataSourceId ? `${baseUrl}/${detectorId}/${dataSourceId}` : `${baseUrl}/${detectorId}`; + return { + type: GET_DETECTOR, + request: (client: HttpSetup) => client.get(url), detectorId + } +}; export const getDetectorList = (queryParams: GetDetectorsQueryParams): APIAction => { - console.log(queryParams); + const baseUrl = `..${AD_NODE_API.DETECTOR}/_list`; + const url = queryParams.dataSourceId ? `${baseUrl}/${queryParams.dataSourceId}` : baseUrl; + return { type: GET_DETECTOR_LIST, - request: (client: HttpSetup) => - client.get(`..${AD_NODE_API.DETECTOR}/${queryParams.dataSourceId}}`, { query: queryParams }), + request: (client: HttpSetup) => client.get(url, { query: queryParams }), }; }; @@ -426,52 +432,69 @@ export const updateDetector = ( detectorId, }); -export const deleteDetector = (detectorId: string, dataSourceId: string): APIAction => ({ - type: DELETE_DETECTOR, - request: (client: HttpSetup) => - client.delete(`..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}`), - detectorId, -}); +export const deleteDetector = (detectorId: string, dataSourceId = ''): APIAction => { + const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; + const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; -export const startDetector = (detectorId: string, dataSourceId: string): APIAction => ({ - type: START_DETECTOR, - request: (client: HttpSetup) => - client.post(`..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}/start`), - detectorId, -}); + return { + type: DELETE_DETECTOR, + request: (client: HttpSetup) => client.delete(url), detectorId, + }; +}; + +export const startDetector = (detectorId: string, dataSourceId = ''): APIAction => { + const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; + const url = dataSourceId ? `${baseUrl}/${dataSourceId}/start` : `${baseUrl}/start`; + + return { + type: START_DETECTOR, + request: (client: HttpSetup) => client.post(url), detectorId, + } +} export const startHistoricalDetector = ( detectorId: string, - dataSourceId: string, + dataSourceId = '', startTime: number, endTime: number -): APIAction => ({ - type: START_HISTORICAL_DETECTOR, - request: (client: HttpSetup) => - client.post(`..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}/start`, { - body: JSON.stringify({ - startTime: startTime, - endTime: endTime, - }), - }), - detectorId, - startTime, - endTime, -}); +): APIAction => { + const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; + const url = dataSourceId ? `${baseUrl}/${dataSourceId}/start` : `${baseUrl}/start`; -export const stopDetector = (detectorId: string, dataSourceId: string): APIAction => ({ - type: STOP_DETECTOR, - request: (client: HttpSetup) => - client.post(`..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}/stop/${false}`), - detectorId, -}); + return { + type: START_HISTORICAL_DETECTOR, + request: (client: HttpSetup) => + client.post(url, { + body: JSON.stringify({ + startTime: startTime, + endTime: endTime, + }), + }), + detectorId, + startTime, + endTime, + }; +}; -export const stopHistoricalDetector = (detectorId: string, dataSourceId: string): APIAction => ({ - type: STOP_HISTORICAL_DETECTOR, - request: (client: HttpSetup) => - client.post(`..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}/stop/${true}`), - detectorId, -}); +export const stopDetector = (detectorId: string, dataSourceId = ''): APIAction => { + const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; + const url = dataSourceId ? `${baseUrl}/${dataSourceId}/stop/${false}` : `${baseUrl}/stop/${false}`; + + return { + type: STOP_DETECTOR, + request: (client: HttpSetup) => client.post(url), detectorId, + }; +}; + +export const stopHistoricalDetector = (detectorId: string, dataSourceId = ''): APIAction => { + const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; + const url = dataSourceId ? `${baseUrl}/${dataSourceId}/stop/${true}` : `${baseUrl}/stop/${true}`; + + return { + type: STOP_HISTORICAL_DETECTOR, + request: (client: HttpSetup) => client.post(url), detectorId, + }; +}; export const getDetectorProfile = (detectorId: string): APIAction => ({ type: GET_DETECTOR_PROFILE, diff --git a/public/redux/reducers/alerting.ts b/public/redux/reducers/alerting.ts index 04e8895c..99aef074 100644 --- a/public/redux/reducers/alerting.ts +++ b/public/redux/reducers/alerting.ts @@ -94,10 +94,15 @@ const reducer = handleActions( initialDetectorsState ); -export const searchMonitors = (): APIAction => ({ - type: SEARCH_MONITORS, - request: (client: HttpSetup) => client.post(`..${ALERTING_NODE_API._SEARCH}`), -}); +export const searchMonitors = ( dataSourceId = ''): APIAction => { + const baseUrl = `..${ALERTING_NODE_API._SEARCH}`; + const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; + + return { + type: SEARCH_MONITORS, + request: (client: HttpSetup) => client.post(url), + }; +}; export const searchAlerts = ( monitorId: string, diff --git a/public/redux/reducers/anomalyResults.ts b/public/redux/reducers/anomalyResults.ts index 2d8b50cd..58a9c32c 100644 --- a/public/redux/reducers/anomalyResults.ts +++ b/public/redux/reducers/anomalyResults.ts @@ -94,73 +94,67 @@ const reducer = handleActions( export const getDetectorResults = ( id: string, - dataSourceId: string, + dataSourceId = '', queryParams: any, isHistorical: boolean, resultIndex: string, onlyQueryCustomResultIndex: boolean -): APIAction => - !resultIndex - ? { - type: DETECTOR_RESULTS, - request: (client: HttpSetup) => - client.get( - `..${AD_NODE_API.DETECTOR}/${id}/${dataSourceId}/results/${isHistorical}`, - { - query: queryParams, - } - ), - } - : { - type: DETECTOR_RESULTS, - request: (client: HttpSetup) => - client.get( - `..${AD_NODE_API.DETECTOR}/${id}/${dataSourceId}/results/${isHistorical}/${resultIndex}/${onlyQueryCustomResultIndex}`, - { - query: queryParams, - } - ), - }; +): APIAction => { + let baseUrl = `..${AD_NODE_API.DETECTOR}/${id}`; + + // append dataSourceId to the url if it has a truthy value + if (dataSourceId) { + baseUrl += `/${dataSourceId}`; + } + // construct the final url based on whether resultIndex is provided + let url = baseUrl + `/results/${isHistorical}`; + if (resultIndex) { + url += `/${resultIndex}/${onlyQueryCustomResultIndex}`; + } + + return { + type: DETECTOR_RESULTS, + request: (client: HttpSetup) => client.get(url, { query: queryParams }), + }; +}; export const searchResults = ( requestBody: any, resultIndex: string, - dataSourceId: string, + dataSourceId = '', onlyQueryCustomResultIndex: boolean -): APIAction => - !resultIndex - ? { - type: SEARCH_ANOMALY_RESULTS, - request: (client: HttpSetup) => - client.post(`..${AD_NODE_API.DETECTOR}/results/${dataSourceId}/_search`, { - body: JSON.stringify(requestBody), - }), - } - : { - type: SEARCH_ANOMALY_RESULTS, - request: (client: HttpSetup) => - client.post( - `..${AD_NODE_API.DETECTOR}/results/${dataSourceId}/_search/${resultIndex}/${onlyQueryCustomResultIndex}`, - { - body: JSON.stringify(requestBody), - } - ), - }; +): APIAction => { + let baseUrl = `..${AD_NODE_API.DETECTOR}/results`; + if (dataSourceId) { + baseUrl += `/${dataSourceId}`; + } + let url = baseUrl + '/_search'; + if (resultIndex) { + url += `/${resultIndex}/${onlyQueryCustomResultIndex}`; + } + + return { + type: SEARCH_ANOMALY_RESULTS, + request: (client: HttpSetup) => client.post(url, { body: JSON.stringify(requestBody) }), + }; +}; export const getTopAnomalyResults = ( detectorId: string, - dataSourceId: string, + dataSourceId: string = '', isHistorical: boolean, requestBody: any -): APIAction => ({ - type: GET_TOP_ANOMALY_RESULTS, - request: (client: HttpSetup) => - client.post( - `..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}/_topAnomalies/${isHistorical}`, - { - body: JSON.stringify(requestBody), - } - ), -}); +): APIAction => { + let baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; + if (dataSourceId) { + baseUrl += `/${dataSourceId}`; + } + const url = `${baseUrl}/_topAnomalies/${isHistorical}`; + + return { + type: GET_TOP_ANOMALY_RESULTS, + request: (client: HttpSetup) => client.post(url, { body: JSON.stringify(requestBody) }), + }; +}; export default reducer; diff --git a/public/redux/reducers/liveAnomalyResults.ts b/public/redux/reducers/liveAnomalyResults.ts index 190dba91..e82291a9 100644 --- a/public/redux/reducers/liveAnomalyResults.ts +++ b/public/redux/reducers/liveAnomalyResults.ts @@ -57,32 +57,24 @@ const reducer = handleActions( export const getDetectorLiveResults = ( detectorId: string, - dataSourceId: string, + dataSourceId: string = '', queryParams: DetectorResultsQueryParams, isHistorical: boolean, resultIndex: string, onlyQueryCustomResultIndex: boolean -): APIAction => - !resultIndex - ? { - type: DETECTOR_LIVE_RESULTS, - request: (client: HttpSetup) => - client.get( - `..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}/results/${isHistorical}`, - { - query: queryParams, - } - ), - } - : { - type: DETECTOR_LIVE_RESULTS, - request: (client: HttpSetup) => - client.get( - `..${AD_NODE_API.DETECTOR}/${detectorId}/${dataSourceId}/results/${isHistorical}/${resultIndex}/${onlyQueryCustomResultIndex}`, - { - query: queryParams, - } - ), - }; +): APIAction => { + let baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; + if (dataSourceId) { + baseUrl += `/${dataSourceId}`; + } + const url = !resultIndex + ? `${baseUrl}/results/${isHistorical}` + : `${baseUrl}/results/${isHistorical}/${resultIndex}/${onlyQueryCustomResultIndex}`; + + return { + type: DETECTOR_LIVE_RESULTS, + request: (client: HttpSetup) => client.get(url, { query: queryParams }), + }; +}; export default reducer; diff --git a/public/redux/reducers/opensearch.ts b/public/redux/reducers/opensearch.ts index a2856edc..3b72f802 100644 --- a/public/redux/reducers/opensearch.ts +++ b/public/redux/reducers/opensearch.ts @@ -246,17 +246,25 @@ const reducer = handleActions( initialState ); -export const getIndices = (searchKey: string = '', dataSourceId: string): APIAction => ({ - type: GET_INDICES, - request: (client: HttpSetup) => - client.get(`..${AD_NODE_API._INDICES}/${dataSourceId}`, { query: { index: searchKey } }), -}); +export const getIndices = (searchKey = '', dataSourceId = '') => { + const baseUrl = `..${AD_NODE_API._INDICES}`; + const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; -export const getAliases = (searchKey: string = ''): APIAction => ({ - type: GET_ALIASES, - request: (client: HttpSetup) => - client.get(`..${AD_NODE_API._ALIASES}`, { query: { alias: searchKey } }), -}); + return { + type: GET_INDICES, + request: (client: HttpSetup) => client.get(url, { query: { index: searchKey } }), + }; +}; + +export const getAliases = (searchKey: string = '', dataSourceId = ''): APIAction => { + const baseUrl = `..${AD_NODE_API._ALIASES}`; + const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; + + return { + type: GET_ALIASES, + request: (client: HttpSetup) => client.get(url, { query: { alias: searchKey } }), + }; +}; export const getMappings = (searchKey: string = ''): APIAction => ({ type: GET_MAPPINGS, @@ -274,19 +282,25 @@ export const searchOpenSearch = (requestData: any): APIAction => ({ }), }); -export const createIndex = (indexConfig: any): APIAction => ({ - type: CREATE_INDEX, - request: (client: HttpSetup) => - client.put(`..${AD_NODE_API.CREATE_INDEX}`, { - body: JSON.stringify(indexConfig), - }), -}); +export const createIndex = (indexConfig: any, dataSourceId = ''): APIAction => { + const url = dataSourceId ? `${AD_NODE_API.CREATE_INDEX}/${dataSourceId}` : AD_NODE_API.CREATE_INDEX; + return { + type: CREATE_INDEX, + request: (client: HttpSetup) => + client.put(`..${url}`, { + body: JSON.stringify(indexConfig), + }), + }; +} -export const bulk = (body: any): APIAction => ({ - type: BULK, - request: (client: HttpSetup) => - client.post(`..${AD_NODE_API.BULK}`, { body: JSON.stringify(body) }), -}); +export const bulk = (body: any, dataSourceId = ''): APIAction => { + const url = dataSourceId ? `${AD_NODE_API.BULK}/${dataSourceId}` : AD_NODE_API.BULK; + return { + type: BULK, + request: (client: HttpSetup) => + client.post(`..${url}`, { body: JSON.stringify(body) }), + }; +}; export const deleteIndex = (index: string): APIAction => ({ type: DELETE_INDEX, @@ -295,11 +309,11 @@ export const deleteIndex = (index: string): APIAction => ({ }); export const getPrioritizedIndices = - (searchKey: string): ThunkAction => + (searchKey: string, dataSourceId = ''): ThunkAction => async (dispatch, getState) => { //Fetch Indices and Aliases with text provided - await dispatch(getIndices(searchKey)); - await dispatch(getAliases(searchKey)); + await dispatch(getIndices(searchKey, dataSourceId)); + await dispatch(getAliases(searchKey, dataSourceId)); const osState = getState().opensearch; const exactMatchedIndices = osState.indices; const exactMatchedAliases = osState.aliases; @@ -311,8 +325,8 @@ export const getPrioritizedIndices = }; } else { //No results found for exact match, append wildCard and get partial matches if exists - await dispatch(getIndices(`${searchKey}*`)); - await dispatch(getAliases(`${searchKey}*`)); + await dispatch(getIndices(`${searchKey}*`, dataSourceId)); + await dispatch(getAliases(`${searchKey}*`, dataSourceId)); const osState = getState().opensearch; const partialMatchedIndices = osState.indices; const partialMatchedAliases = osState.aliases; diff --git a/public/redux/reducers/sampleData.ts b/public/redux/reducers/sampleData.ts index 549516a6..8511c19c 100644 --- a/public/redux/reducers/sampleData.ts +++ b/public/redux/reducers/sampleData.ts @@ -54,10 +54,16 @@ const reducer = handleActions( initialState ); -export const createSampleData = (sampleDataType: SAMPLE_TYPE): APIAction => ({ - type: CREATE_SAMPLE_DATA, - request: (client: HttpSetup) => - client.post(`..${AD_NODE_API.CREATE_SAMPLE_DATA}/${sampleDataType}`), -}); +export const createSampleData = (sampleDataType: SAMPLE_TYPE, dataSourceId = ''): APIAction => { + const url = dataSourceId ? + `..${AD_NODE_API.CREATE_SAMPLE_DATA}/${sampleDataType}/${dataSourceId}` : + `..${AD_NODE_API.CREATE_SAMPLE_DATA}/${sampleDataType}`; + + return { + type: CREATE_SAMPLE_DATA, + request: (client: HttpSetup) => + client.post(url), + }; +}; export default reducer; diff --git a/public/services.ts b/public/services.ts index ec164c14..f79e8cc3 100644 --- a/public/services.ts +++ b/public/services.ts @@ -10,6 +10,7 @@ import { OverlayStart, } from '../../../src/core/public'; import { DataPublicPluginStart } from '../../../src/plugins/data/public'; +import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public'; import { EmbeddableStart } from '../../../src/plugins/embeddable/public'; import { createGetterSetter } from '../../../src/plugins/opensearch_dashboards_utils/public'; import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; @@ -42,6 +43,9 @@ export const [getQueryService, setQueryService] = export const [getSavedObjectsClient, setSavedObjectsClient] = createGetterSetter('SavedObjectsClient'); +export const [getDataSourceManagementPlugin, setDataSourceManagementPlugin] = + createGetterSetter('DataSourceManagement'); + // This is primarily used for mocking this module and each of its fns in tests. export default { getSavedFeatureAnywhereLoader, diff --git a/server/plugin.ts b/server/plugin.ts index bffec10e..5736a3bf 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -39,8 +39,8 @@ import { DataSourcePluginSetup } from '../../../src/plugins/data_source/server/t import { DataSourceManagementPlugin } from '../../../src/plugins/data_source_management/public'; export interface ADPluginSetupDependencies { - dataSourceManagement: ReturnType; - dataSource: DataSourcePluginSetup; + dataSourceManagement?: ReturnType; + dataSource?: DataSourcePluginSetup; } export class AnomalyDetectionOpenSearchDashboardsPlugin diff --git a/server/routes/ad.ts b/server/routes/ad.ts index 1436f394..b04343cd 100644 --- a/server/routes/ad.ts +++ b/server/routes/ad.ts @@ -60,43 +60,85 @@ type PutDetectorParams = { }; export function registerADRoutes(apiRouter: Router, adService: AdService) { + // create detector apiRouter.post('/detectors', adService.putDetector); - apiRouter.put('/detectors/{detectorId}', adService.putDetector); + apiRouter.post('/detectors/{dataSourceId}', adService.putDetector); + + // put detector + apiRouter.put('/detectors/{detectorId}', + adService.putDetector); + apiRouter.put('/detectors/{detectorId}/{dataSourceId}', + adService.putDetector); + apiRouter.post('/detectors/_search', adService.searchDetector); - apiRouter.post('/detectors/results/_search/', adService.searchResults); - apiRouter.post('/detectors/results/{dataSourceId}/_search', adService.searchResults); - apiRouter.post( - '/detectors/results/{dataSourceId}/_search/{resultIndex}/{onlyQueryCustomResultIndex}', - adService.searchResults - ); - apiRouter.get('/detectors/{dataSourceId}', adService.getDetectors); + + + // post search anomaly results + apiRouter.post('/detectors/results/_search/', + adService.searchResults); + apiRouter.post('/detectors/results/{dataSourceId}/_search', + adService.searchResults); + apiRouter.post('/detectors/results/_search/{resultIndex}/{onlyQueryCustomResultIndex}', + adService.searchResults); + apiRouter.post('/detectors/results/{dataSourceId}/_search/{resultIndex}/{onlyQueryCustomResultIndex}', + adService.searchResults); + + // list detectors + apiRouter.get('/detectors/_list', + adService.getDetectors); + apiRouter.get('/detectors/_list/{dataSourceId}', + adService.getDetectors); + apiRouter.post('/detectors/preview', adService.previewDetector); - apiRouter.get( - '/detectors/{id}/{dataSourceId}/results/{isHistorical}/{resultIndex}/{onlyQueryCustomResultIndex}', - adService.getAnomalyResults - ); - apiRouter.get( - '/detectors/{id}/{dataSourceId}/results/{isHistorical}', - adService.getAnomalyResults - ); - apiRouter.delete('/detectors/{detectorId}/{dataSourceId}', adService.deleteDetector); - apiRouter.post('/detectors/{detectorId}/{dataSourceId}/start', adService.startDetector); - apiRouter.post( - '/detectors/{detectorId}/{dataSourceId}/stop/{isHistorical}', - adService.stopDetector - ); + + // get detector anomaly results + apiRouter.get('/detectors/{id}/results/{isHistorical}/{resultIndex}/{onlyQueryCustomResultIndex}', + adService.getAnomalyResults); + apiRouter.get('/detectors/{id}/{dataSourceId}/results/{isHistorical}/{resultIndex}/{onlyQueryCustomResultIndex}', + adService.getAnomalyResults); + apiRouter.get('/detectors/{id}/results/{isHistorical}', + adService.getAnomalyResults); + apiRouter.get('/detectors/{id}/{dataSourceId}/results/{isHistorical}', + adService.getAnomalyResults); + + // delete detector + apiRouter.delete('/detectors/{detectorId}', + adService.deleteDetector); + apiRouter.delete('/detectors/{detectorId}/{dataSourceId}', + adService.deleteDetector); + + // start detector + apiRouter.post('/detectors/{detectorId}/start', + adService.startDetector); + apiRouter.post('/detectors/{detectorId}/{dataSourceId}/start', + adService.startDetector); + + // stop detector + apiRouter.post('/detectors/{detectorId}/stop/{isHistorical}', + adService.stopDetector); + apiRouter.post('/detectors/{detectorId}/{dataSourceId}/stop/{isHistorical}', + adService.stopDetector); + apiRouter.get( '/detectors/{detectorId}/_profile', adService.getDetectorProfile ); - apiRouter.get('/detectors/{detectorId}/{dataSourceId}', adService.getDetector); + + // get detector + apiRouter.get('/detectors/{detectorId}', + adService.getDetector); + apiRouter.get('/detectors/{detectorId}/{dataSourceId}', + adService.getDetector); apiRouter.get('/detectors/{detectorName}/_match', adService.matchDetector); apiRouter.get('/detectors/_count', adService.getDetectorCount); - apiRouter.post( - '/detectors/{detectorId}/{dataSourceId}/_topAnomalies/{isHistorical}', - adService.getTopAnomalyResults - ); + + // post get top anomaly results + apiRouter.post('/detectors/{detectorId}/_topAnomalies/{isHistorical}', + adService.getTopAnomalyResults); + apiRouter.post('/detectors/{detectorId}/{dataSourceId}/_topAnomalies/{isHistorical}', + adService.getTopAnomalyResults); + apiRouter.post( '/detectors/_validate/{validationType}', adService.validateDetector @@ -119,7 +161,7 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; - const { dataSourceId } = request.params as { dataSourceId: string }; + const { dataSourceId = "" } = request.params as { dataSourceId?: string }; const callWithRequest = getClientBasedOnDataSource( context, this.dataSourceEnabled, @@ -189,6 +231,8 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; + const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + //@ts-ignore const ifSeqNo = request.body.seqNo; //@ts-ignore @@ -205,14 +249,18 @@ export default class AdService { }; let response; + const callWithRequest = getClientBasedOnDataSource( + context, + this.dataSourceEnabled, + request, + dataSourceId, + this.client); + + if (isNumber(ifSeqNo) && isNumber(ifPrimaryTerm)) { - response = await this.client - .asScoped(request) - .callAsCurrentUser('ad.updateDetector', params); + response = await callWithRequest('ad.updateDetector', params); } else { - response = await this.client - .asScoped(request) - .callAsCurrentUser('ad.createDetector', { + response = await callWithRequest('ad.createDetector', { body: params.body, }); } @@ -281,7 +329,7 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; - const { dataSourceId } = request.params as { dataSourceId: string }; + const { dataSourceId = "" } = request.params as { dataSourceId?: string }; const callWithRequest = getClientBasedOnDataSource( context, this.dataSourceEnabled, @@ -389,7 +437,7 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; - const { dataSourceId } = request.params as { dataSourceId: string }; + const { dataSourceId = "" } = request.params as { dataSourceId?: string }; //@ts-ignore const startTime = request.body?.startTime; //@ts-ignore @@ -440,11 +488,12 @@ export default class AdService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { try { - let { detectorId, dataSourceId, isHistorical } = request.params as { + let { detectorId, isHistorical } = request.params as { detectorId: string; - dataSourceId: string; isHistorical: any; }; + const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + isHistorical = JSON.parse(isHistorical) as boolean; const requestPath = isHistorical ? 'ad.stopHistoricalDetector' @@ -556,11 +605,12 @@ export default class AdService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { try { - var { resultIndex, dataSourceId, onlyQueryCustomResultIndex } = request.params as { + var { resultIndex, onlyQueryCustomResultIndex } = request.params as { resultIndex: string; - dataSourceId: string; onlyQueryCustomResultIndex: boolean; }; + const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + if ( !resultIndex || !resultIndex.startsWith(CUSTOM_AD_RESULT_INDEX_PREFIX) @@ -859,14 +909,15 @@ export default class AdService { request: OpenSearchDashboardsRequest, opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { - let { id, dataSourceId, isHistorical, resultIndex, onlyQueryCustomResultIndex } = + let { id, isHistorical, resultIndex, onlyQueryCustomResultIndex } = request.params as { id: string; - dataSourceId: string; isHistorical: any; resultIndex: string; onlyQueryCustomResultIndex: boolean; }; + const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + if ( !resultIndex || !resultIndex.startsWith(CUSTOM_AD_RESULT_INDEX_PREFIX) @@ -1099,11 +1150,12 @@ export default class AdService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { try { - let { detectorId, dataSourceId, isHistorical } = request.params as { + let { detectorId, isHistorical } = request.params as { detectorId: string; - dataSourceId: string; isHistorical: any; }; + const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + isHistorical = JSON.parse(isHistorical) as boolean; const requestPath = isHistorical ? 'ad.topHistoricalAnomalyResults' diff --git a/server/routes/alerting.ts b/server/routes/alerting.ts index 794b973a..0da07a72 100644 --- a/server/routes/alerting.ts +++ b/server/routes/alerting.ts @@ -29,6 +29,8 @@ export function registerAlertingRoutes( alertingService: AlertingService ) { apiRouter.post('/monitors/_search', alertingService.searchMonitors); + apiRouter.post('/monitors/_search/{dataSourceId}', alertingService.searchMonitors); + apiRouter.get('/monitors/alerts', alertingService.searchAlerts); } @@ -47,6 +49,8 @@ export default class AlertingService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { try { + const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + const requestBody = { size: MAX_MONITORS, query: { @@ -79,7 +83,7 @@ export default class AlertingService { context, this.dataSourceEnabled, request, - '4585f560-d1ef-11ee-aa63-2181676cc573', + dataSourceId, this.client); const response: SearchResponse = await callWithRequest( @@ -133,16 +137,9 @@ export default class AlertingService { startTime?: number; endTime?: number; }; - - const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, - '4585f560-d1ef-11ee-aa63-2181676cc573', - this.client); - - const response = await callWithRequest( - 'alerting.searchAlerts', { + const response = await this.client + .asScoped(request) + .callAsCurrentUser('alerting.searchAlerts', { monitorId: monitorId, startTime: startTime, endTime: endTime, @@ -163,4 +160,4 @@ export default class AlertingService { }); } }; -} +} \ No newline at end of file diff --git a/server/routes/opensearch.ts b/server/routes/opensearch.ts index 1444dbc7..cdc647d2 100644 --- a/server/routes/opensearch.ts +++ b/server/routes/opensearch.ts @@ -39,12 +39,21 @@ export function registerOpenSearchRoutes( apiRouter: Router, opensearchService: OpenSearchService ) { + apiRouter.get('/_indices', opensearchService.getIndices); apiRouter.get('/_indices/{dataSourceId}', opensearchService.getIndices); + apiRouter.get('/_aliases', opensearchService.getAliases); + apiRouter.get('/_aliases/{dataSourceId}', opensearchService.getAliases); + apiRouter.get('/_mappings', opensearchService.getMapping); apiRouter.post('/_search', opensearchService.executeSearch); + apiRouter.put('/create_index', opensearchService.createIndex); + apiRouter.put('/create_index/{dataSourceId}', opensearchService.createIndex); + apiRouter.post('/bulk', opensearchService.bulk); + apiRouter.post('/bulk/{dataSourceId}', opensearchService.bulk); + apiRouter.post('/delete_index', opensearchService.deleteIndex); } @@ -91,14 +100,9 @@ export default class OpenSearchService { const params: SearchParams = { index, size, body: requestBody }; - const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, - '4585f560-d1ef-11ee-aa63-2181676cc573', - this.client); - - const results: SearchResponse = await callWithRequest('search', params); + const results: SearchResponse = await this.client + .asScoped(request) + .callAsCurrentUser('search', params); return opensearchDashboardsResponse.ok({ body: { ok: true, response: results }, @@ -120,7 +124,7 @@ export default class OpenSearchService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { const { index } = request.query as { index: string }; - const { dataSourceId } = request.params as { dataSourceId: string }; + const { dataSourceId = "" } = request.params as { dataSourceId?: string }; try { const callWithRequest = getClientBasedOnDataSource( context, @@ -165,12 +169,14 @@ export default class OpenSearchService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { const { alias } = request.query as { alias: string }; + const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + try { const callWithRequest = getClientBasedOnDataSource( context, this.dataSourceEnabled, request, - '4585f560-d1ef-11ee-aa63-2181676cc573', + dataSourceId, this.client); const response: IndexAlias[] = await callWithRequest( @@ -198,6 +204,8 @@ export default class OpenSearchService { request: OpenSearchDashboardsRequest, opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { + const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + //@ts-ignore const index = request.body.index; //@ts-ignore @@ -206,7 +214,7 @@ export default class OpenSearchService { context, this.dataSourceEnabled, request, - '4585f560-d1ef-11ee-aa63-2181676cc573', + dataSourceId, this.client); try { await callWithRequest('indices.create', { @@ -247,13 +255,14 @@ export default class OpenSearchService { request: OpenSearchDashboardsRequest, opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { + const { dataSourceId = "" } = request.params as { dataSourceId?: string }; const body = request.body; try { const callWithRequest = getClientBasedOnDataSource( context, this.dataSourceEnabled, request, - '4585f560-d1ef-11ee-aa63-2181676cc573', + dataSourceId, this.client); const response: any = await callWithRequest('bulk', { @@ -279,13 +288,6 @@ export default class OpenSearchService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { const index = request.query as { index: string }; - const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, - '4585f560-d1ef-11ee-aa63-2181676cc573', - this.client); - try { await callWithRequest('indices.delete', { index: index, @@ -306,8 +308,9 @@ export default class OpenSearchService { } } try { - const response: CatIndex[] = await callWithRequest( - 'cat.indices', { + const response: CatIndex[] = await this.client + .asScoped(request) + .callAsCurrentUser('cat.indices', { index, format: 'json', h: 'health,index', @@ -333,15 +336,9 @@ export default class OpenSearchService { ): Promise> => { const { index } = request.query as { index: string }; try { - const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, - '4585f560-d1ef-11ee-aa63-2181676cc573', - this.client); - - const response = await callWithRequest( - 'indices.getMapping', { + const response = await this.client + .asScoped(request) + .callAsCurrentUser('indices.getMapping', { index, }); return opensearchDashboardsResponse.ok({ diff --git a/server/routes/sampleData.ts b/server/routes/sampleData.ts index e1881df2..303ce486 100644 --- a/server/routes/sampleData.ts +++ b/server/routes/sampleData.ts @@ -27,10 +27,10 @@ export function registerSampleDataRoutes( apiRouter: Router, sampleDataService: SampleDataService ) { - apiRouter.post( - '/create_sample_data/{type}', - sampleDataService.createSampleData - ); + apiRouter.post('/create_sample_data/{type}', + sampleDataService.createSampleData); + apiRouter.post('/create_sample_data/{type}/{dataSourceId}', + sampleDataService.createSampleData); } export default class SampleDataService { @@ -81,7 +81,7 @@ export default class SampleDataService { } } - await loadSampleData(filePath, indexName, this.client, request); + await loadSampleData(filePath, indexName, this.client, request, context, this.dataSourceEnabled); return opensearchDashboardsResponse.ok({ body: { ok: true } }); } catch (err) { diff --git a/server/sampleData/utils/helpers.ts b/server/sampleData/utils/helpers.ts index 86bdd04a..42a37eba 100644 --- a/server/sampleData/utils/helpers.ts +++ b/server/sampleData/utils/helpers.ts @@ -19,7 +19,7 @@ import { import fs from 'fs'; import { createUnzip } from 'zlib'; import { isEmpty } from 'lodash'; -import { prettifyErrorMessage } from '../../utils/helpers'; +import { getClientBasedOnDataSource, prettifyErrorMessage } from '../../utils/helpers'; const BULK_INSERT_SIZE = 500; @@ -27,8 +27,12 @@ export const loadSampleData = ( filePath: string, indexName: string, client: any, - request: OpenSearchDashboardsRequest + request: OpenSearchDashboardsRequest, + context: RequestHandlerContext, + dataSourceEnabled: boolean ) => { + const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + return new Promise((resolve, reject) => { let count: number = 0; let docs: any[] = []; @@ -95,10 +99,17 @@ export const loadSampleData = ( } }); + const callWithRequest = getClientBasedOnDataSource( + context, + dataSourceEnabled, + request, + dataSourceId, + client); + const bulkInsert = async (docs: any[]) => { try { const bulkBody = prepareBody(docs, offset); - const resp = await client.asScoped(request).callAsCurrentUser('bulk', { + const resp = await callWithRequest('bulk', { body: bulkBody, }); if (resp.errors) { From 373bbdfd3a4ae893c794137b6c8bc1b4337d2927 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Fri, 12 Apr 2024 10:28:48 -0700 Subject: [PATCH 14/26] change version back to 3.0 Signed-off-by: Jackie Han --- opensearch_dashboards.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index 9abd7894..c62acf46 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -1,7 +1,7 @@ { "id": "anomalyDetectionDashboards", - "version": "2.11.0.0", - "opensearchDashboardsVersion": "2.11.0", + "version": "3.0.0.0", + "opensearchDashboardsVersion": "3.0.0", "configPath": [ "anomaly_detection_dashboards" ], From e046f6d587d96e13b3f5027dc51f84d1ecb1fd92 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Fri, 12 Apr 2024 10:29:44 -0700 Subject: [PATCH 15/26] revert version change Signed-off-by: Jackie Han --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 54f91cb6..97b79f4d 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "anomaly-detection-dashboards", - "version": "2.11.0.0", + "version": "3.0.0.0", "description": "OpenSearch Anomaly Detection Dashboards Plugin", "main": "index.js", "config": { - "plugin_version": "2.11.0.0", + "plugin_version": "3.0.0.0", "plugin_name": "anomalyDetectionDashboards", "plugin_zip_name": "anomaly-detection-dashboards" }, From d1bbbaea61878c5f1d37b6553906456ec044a0b1 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Fri, 12 Apr 2024 10:30:53 -0700 Subject: [PATCH 16/26] make dataSourceId optional in DetectorListItem type Signed-off-by: Jackie Han --- public/models/interfaces.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/models/interfaces.ts b/public/models/interfaces.ts index 881e614a..ff578f12 100644 --- a/public/models/interfaces.ts +++ b/public/models/interfaces.ts @@ -219,7 +219,7 @@ export type DetectorListItem = { lastUpdateTime: number; enabledTime?: number; detectorType?: string; - dataSourceId: string; + dataSourceId?: string; }; export type EntityData = { From 894cf08d6c35fcf091d119db65d32937d4cc21c9 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Fri, 12 Apr 2024 10:34:48 -0700 Subject: [PATCH 17/26] update imports Signed-off-by: Jackie Han --- public/pages/Dashboard/Container/DashboardOverview.tsx | 3 +-- public/pages/utils/constants.ts | 10 ---------- public/pages/utils/helpers.ts | 10 ---------- 3 files changed, 1 insertion(+), 22 deletions(-) diff --git a/public/pages/Dashboard/Container/DashboardOverview.tsx b/public/pages/Dashboard/Container/DashboardOverview.tsx index 647b8ad7..5a2604c1 100644 --- a/public/pages/Dashboard/Container/DashboardOverview.tsx +++ b/public/pages/Dashboard/Container/DashboardOverview.tsx @@ -33,11 +33,10 @@ import { ALL_DETECTORS_MESSAGE, ALL_DETECTOR_STATES_MESSAGE, ALL_INDICES_MESSAGE, - getAllDetectorsQueryParamsWithDataSourceId } from '../utils/constants'; import { AppState } from '../../../redux/reducers'; import { CatIndex, IndexAlias, MDSQueryParams } from '../../../../server/models/types'; -import { getVisibleOptions } from '../../utils/helpers'; +import { getAllDetectorsQueryParamsWithDataSourceId, getVisibleOptions } from '../../utils/helpers'; import { BREADCRUMBS } from '../../../utils/constants'; import { DETECTOR_STATE } from '../../../../server/utils/constants'; import { getDetectorStateOptions, getURLQueryParams } from '../../DetectorsList/utils/helpers'; diff --git a/public/pages/utils/constants.ts b/public/pages/utils/constants.ts index 335d9b1c..15f3b5b1 100644 --- a/public/pages/utils/constants.ts +++ b/public/pages/utils/constants.ts @@ -65,16 +65,6 @@ export const GET_ALL_DETECTORS_QUERY_PARAMS = { sortField: 'name', }; -export const getAllDetectorsQueryParamsWithDataSourceId = (dataSourceId: string) => ({ - from: 0, - search: '', - indices: '', - size: MAX_DETECTORS, - sortDirection: SORT_DIRECTION.ASC, - sortField: 'name', - dataSourceId: dataSourceId, -}); - export const getSampleDetectorsQueryParamsWithDataSouceId = (dataSourceId: string) => ( { diff --git a/public/pages/utils/helpers.ts b/public/pages/utils/helpers.ts index 879b843c..9d15e3a0 100644 --- a/public/pages/utils/helpers.ts +++ b/public/pages/utils/helpers.ts @@ -128,13 +128,3 @@ export const getAllDetectorsQueryParamsWithDataSourceId = ( sortField: 'name', dataSourceId: dataSourceId, }); - -export const getMDSQueryParams = (location: { - search: string -}): MDSQueryParams => { - const params = new URLSearchParams(location.search); - const dataSourceId = params.get('dataSourceId'); - return { - dataSourceId: dataSourceId || '', - }; - } From 2cea06649991261f56d81fb6267c2320a38b1618 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Fri, 12 Apr 2024 10:36:05 -0700 Subject: [PATCH 18/26] remove used function Signed-off-by: Jackie Han --- public/pages/Dashboard/utils/constants.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/public/pages/Dashboard/utils/constants.ts b/public/pages/Dashboard/utils/constants.ts index bbc36680..e673de09 100644 --- a/public/pages/Dashboard/utils/constants.ts +++ b/public/pages/Dashboard/utils/constants.ts @@ -81,16 +81,6 @@ export const GET_ALL_DETECTORS_QUERY_PARAMS = { sortField: 'name', }; -export const getAllDetectorsQueryParamsWithDataSourceId = (dataSourceId: string) => ({ - from: 0, - search: '', - indices: '', - size: MAX_DETECTORS, - sortDirection: SORT_DIRECTION.ASC, - sortField: 'name', - dataSourceId: dataSourceId, -}); - export const ALL_DETECTORS_MESSAGE = 'All detectors'; export const ALL_DETECTOR_STATES_MESSAGE = 'All detector states'; export const ALL_INDICES_MESSAGE = 'All indices'; From 2189a54b2458a9beec904c48238f387b2fd0d246 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Fri, 12 Apr 2024 10:46:36 -0700 Subject: [PATCH 19/26] cleanup Signed-off-by: Jackie Han --- .../components/Datasource/DataSource.tsx | 13 +------------ .../NameAndDescription/NameAndDescription.tsx | 15 +-------------- .../DefineDetector/containers/DefineDetector.tsx | 2 -- .../DetectorDetail/containers/DetectorDetail.tsx | 1 - .../components/ListFilters/ListFilters.tsx | 4 ---- .../containers/AnomalyDetectionOverview.tsx | 3 +-- public/pages/main/Main.tsx | 7 ++----- public/pages/utils/constants.ts | 12 ------------ public/pages/utils/helpers.ts | 13 ++++++++++++- public/redux/reducers/ad.ts | 2 +- 10 files changed, 18 insertions(+), 54 deletions(-) diff --git a/public/pages/DefineDetector/components/Datasource/DataSource.tsx b/public/pages/DefineDetector/components/Datasource/DataSource.tsx index a65c6e8e..fdb810e3 100644 --- a/public/pages/DefineDetector/components/Datasource/DataSource.tsx +++ b/public/pages/DefineDetector/components/Datasource/DataSource.tsx @@ -32,7 +32,6 @@ import { DetectorDefinitionFormikValues } from '../../models/interfaces'; import { ModelConfigurationFormikValues } from '../../../ConfigureModel/models/interfaces'; import { INITIAL_MODEL_CONFIGURATION_VALUES } from '../../../ConfigureModel/utils/constants'; import { FILTER_TYPES } from '../../../../models/interfaces'; -import { getNotifications, getSavedObjectsClient } from '../../../../services'; interface DataSourceProps { formikProps: FormikProps; @@ -45,13 +44,6 @@ interface DataSourceProps { } export function DataSource(props: DataSourceProps) { - const [selectedDataSource, setSelectedDataSource] = useState(); - - const onSelectedDataSource = (e) => { - const dataConnectionId = e[0] ? e[0].id : undefined; - setSelectedDataSource(dataConnectionId); - console.log(dataConnectionId); - } const dispatch = useDispatch(); const [indexName, setIndexName] = useState( props.formikProps.values.index[0]?.label @@ -103,7 +95,6 @@ export function DataSource(props: DataSourceProps) { return ( - {props.isEdit && isDifferentIndex() ? (
    ) : null} - {({ field, form }: FieldProps) => { return ( -
    ); -} +} \ No newline at end of file diff --git a/public/pages/DefineDetector/components/NameAndDescription/NameAndDescription.tsx b/public/pages/DefineDetector/components/NameAndDescription/NameAndDescription.tsx index 530cb0cf..a85cf3b0 100644 --- a/public/pages/DefineDetector/components/NameAndDescription/NameAndDescription.tsx +++ b/public/pages/DefineDetector/components/NameAndDescription/NameAndDescription.tsx @@ -16,23 +16,11 @@ import ContentPanel from '../../../../components/ContentPanel/ContentPanel'; import { getError, isInvalid } from '../../../../utils/utils'; import { validateDetectorDesc } from './utils/validation'; import { FormattedFormRow } from '../../../../components/FormattedFormRow/FormattedFormRow'; -import { useState } from 'react'; -import { getNotifications, getSavedObjectsClient } from '../../../../services'; -import { ClusterSelector } from '../../../../../../../src/plugins/data_source_management/public'; interface NameAndDescriptionProps { onValidateDetectorName: (detectorName: string) => Promise; } - - function NameAndDescription(props: NameAndDescriptionProps) { - const [selectedDataSource, setSelectedDataSource] = useState(); - - const onSelectedDataSource = (e) => { - const dataConnectionId = e[0] ? e[0].id : undefined; - setSelectedDataSource(dataConnectionId); - console.log(dataConnectionId); - } return ( @@ -56,7 +44,6 @@ function NameAndDescription(props: NameAndDescriptionProps) { )} - {({ field, form }: FieldProps) => ( {
    - - void; } export const ListFilters = (props: ListFiltersProps) => ( - ( fullWidth={true} /> - ( fullWidth={true} /> - - {props.pageCount > 1 ? ( ) } - /> - - - + /> diff --git a/public/pages/utils/constants.ts b/public/pages/utils/constants.ts index 15f3b5b1..ad03e153 100644 --- a/public/pages/utils/constants.ts +++ b/public/pages/utils/constants.ts @@ -65,18 +65,6 @@ export const GET_ALL_DETECTORS_QUERY_PARAMS = { sortField: 'name', }; - -export const getSampleDetectorsQueryParamsWithDataSouceId = (dataSourceId: string) => ( - { - from: 0, - search: 'sample', - indices: '', - size: MAX_DETECTORS, - sortDirection: SORT_DIRECTION.ASC, - sortField: 'name', - dataSourceId: dataSourceId, -}); - export const GET_SAMPLE_DETECTORS_QUERY_PARAMS = { from: 0, search: 'sample', diff --git a/public/pages/utils/helpers.ts b/public/pages/utils/helpers.ts index 9d15e3a0..2d38d497 100644 --- a/public/pages/utils/helpers.ts +++ b/public/pages/utils/helpers.ts @@ -9,7 +9,7 @@ * GitHub history for details. */ -import { CatIndex, IndexAlias, MDSQueryParams } from '../../../server/models/types'; +import { CatIndex, IndexAlias } from '../../../server/models/types'; import sortBy from 'lodash/sortBy'; import { DetectorListItem } from '../../models/interfaces'; import { SORT_DIRECTION } from '../../../server/utils/constants'; @@ -128,3 +128,14 @@ export const getAllDetectorsQueryParamsWithDataSourceId = ( sortField: 'name', dataSourceId: dataSourceId, }); + +export const getSampleDetectorsQueryParamsWithDataSouceId = (dataSourceId: string) => ( + { + from: 0, + search: 'sample', + indices: '', + size: MAX_DETECTORS, + sortDirection: SORT_DIRECTION.ASC, + sortField: 'name', + dataSourceId: dataSourceId, +}); diff --git a/public/redux/reducers/ad.ts b/public/redux/reducers/ad.ts index 4bb43e35..1191cf8f 100644 --- a/public/redux/reducers/ad.ts +++ b/public/redux/reducers/ad.ts @@ -18,7 +18,7 @@ import { import handleActions from '../utils/handleActions'; import { Detector, DetectorListItem } from '../../models/interfaces'; import { AD_NODE_API } from '../../../utils/constants'; -import { GetDetectorsQueryParams, MDSQueryParams } from '../../../server/models/types'; +import { GetDetectorsQueryParams } from '../../../server/models/types'; import { cloneDeep, get } from 'lodash'; import moment from 'moment'; import { DETECTOR_STATE } from '../../../server/utils/constants'; From 7b428f8ef0c76e41b8f6253bd8523da2abf5536d Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Fri, 12 Apr 2024 14:23:32 -0700 Subject: [PATCH 20/26] add getter and setter for dataSource plugin Signed-off-by: Jackie Han --- public/anomaly_detection_app.tsx | 3 --- .../pages/Dashboard/Container/DashboardOverview.tsx | 9 +++++---- .../DetectorDetail/containers/DetectorDetail.tsx | 7 +++---- public/pages/DetectorsList/containers/List/List.tsx | 13 +++++++------ .../containers/AnomalyDetectionOverview.tsx | 10 ++++------ public/pages/main/Main.tsx | 9 +-------- public/plugin.ts | 7 +++++-- public/services.ts | 4 ++++ 8 files changed, 29 insertions(+), 33 deletions(-) diff --git a/public/anomaly_detection_app.tsx b/public/anomaly_detection_app.tsx index 25546899..37e2e5f4 100644 --- a/public/anomaly_detection_app.tsx +++ b/public/anomaly_detection_app.tsx @@ -17,12 +17,10 @@ import { Main } from './pages/main'; import { Provider } from 'react-redux'; import configureStore from './redux/configureStore'; import { CoreServicesContext } from './components/CoreServices/CoreServices'; -import { DataSourcePluginSetup } from '../../../src/plugins/data_source/public'; export function renderApp( coreStart: CoreStart, params: AppMountParameters, - dataSource: DataSourcePluginSetup ) { const http = coreStart.http; const store = configureStore(http); @@ -42,7 +40,6 @@ export function renderApp( render={(props) => (
    diff --git a/public/pages/Dashboard/Container/DashboardOverview.tsx b/public/pages/Dashboard/Container/DashboardOverview.tsx index 5a2604c1..3febf2fe 100644 --- a/public/pages/Dashboard/Container/DashboardOverview.tsx +++ b/public/pages/Dashboard/Container/DashboardOverview.tsx @@ -49,11 +49,10 @@ import { import { CoreServicesContext } from '../../../components/CoreServices/CoreServices'; import { CoreStart, MountPoint } from '../../../../../../src/core/public'; import { DataSourceSelectableConfig } from '../../../../../../src/plugins/data_source_management/public'; -import { getDataSourceManagementPlugin, getNotifications, getSavedObjectsClient } from '../../../services'; +import { getDataSourceManagementPlugin, getDataSourcePlugin, getNotifications, getSavedObjectsClient } from '../../../services'; import { RouteComponentProps } from 'react-router-dom'; interface OverviewProps extends RouteComponentProps { - dataSourceEnabled: boolean; setActionMenu: (menuMount: MountPoint | undefined) => void; } @@ -71,6 +70,8 @@ export function DashboardOverview(props: OverviewProps) { const errorGettingDetectors = adState.errorMessage; const isLoadingDetectors = adState.requesting; + const dataSourceEnabled = getDataSourcePlugin().dataSourceEnabled; + const [currentDetectors, setCurrentDetectors] = useState( Object.values(allDetectorList) ); @@ -204,7 +205,7 @@ export function DashboardOverview(props: OverviewProps) { ...location, search: queryString.stringify(updatedParams), }) - if (props.dataSourceEnabled ? MDSOverviewState.selectedDataSourceId : true) { + if (dataSourceEnabled ? MDSOverviewState.selectedDataSourceId : true) { intializeDetectors(); } }, [MDSOverviewState]); @@ -262,7 +263,7 @@ export function DashboardOverview(props: OverviewProps) { return (
    - {renderDataSourceComponent} + {dataSourceEnabled && renderDataSourceComponent} 0} /> {isLoadingDetectors ? (
    diff --git a/public/pages/DetectorDetail/containers/DetectorDetail.tsx b/public/pages/DetectorDetail/containers/DetectorDetail.tsx index 69760125..b70206a8 100644 --- a/public/pages/DetectorDetail/containers/DetectorDetail.tsx +++ b/public/pages/DetectorDetail/containers/DetectorDetail.tsx @@ -59,13 +59,12 @@ import { DETECTOR_STATE } from '../../../../server/utils/constants'; import { CatIndex } from '../../../../server/models/types'; import { containsIndex } from '../utils/helpers'; import { DataSourceViewConfig } from '../../../../../../src/plugins/data_source_management/public'; -import { getDataSourceManagementPlugin, getNotifications, getSavedObjectsClient } from '../../../services'; +import { getDataSourceManagementPlugin, getDataSourcePlugin, getNotifications, getSavedObjectsClient } from '../../../services'; export interface DetectorRouterProps { detectorId?: string; } interface DetectorDetailProps extends RouteComponentProps { - dataSourceEnabled: boolean; setActionMenu: (menuMount: MountPoint | undefined) => void; } @@ -107,14 +106,14 @@ export const DetectorDetail = (props: DetectorDetailProps) => { const core = React.useContext(CoreServicesContext) as CoreStart; const dispatch = useDispatch(); const detectorId = get(props, 'match.params.detectorId', '') as string; - + const dataSourceEnabled = getDataSourcePlugin().dataSourceEnabled; const location = useLocation(); const queryParams = new URLSearchParams(location.search); const dataSourceId = queryParams.get('dataSourceId') as string; const { detector, hasError, isLoadingDetector, errorMessage } = useFetchDetectorInfo(detectorId, dataSourceId); - const { monitor} = useFetchMonitorInfo(detectorId, dataSourceId, props.dataSourceEnabled); + const { monitor} = useFetchMonitorInfo(detectorId, dataSourceId, dataSourceEnabled); const visibleIndices = useSelector( (state: AppState) => state.opensearch.indices ) as CatIndex[]; diff --git a/public/pages/DetectorsList/containers/List/List.tsx b/public/pages/DetectorsList/containers/List/List.tsx index f1c1f058..d93c3230 100644 --- a/public/pages/DetectorsList/containers/List/List.tsx +++ b/public/pages/DetectorsList/containers/List/List.tsx @@ -84,7 +84,7 @@ import { import { CoreStart, MountPoint } from '../../../../../../../src/core/public'; import { CoreServicesContext } from '../../../../components/CoreServices/CoreServices'; import { DataSourceSelectableConfig } from '../../../../../../../src/plugins/data_source_management/public'; -import { getDataSourceManagementPlugin, getNotifications, getSavedObjectsClient } from '../../../../services'; +import { getDataSourceManagementPlugin, getDataSourcePlugin, getNotifications, getSavedObjectsClient } from '../../../../services'; export interface ListRouterParams { from: string; @@ -96,7 +96,6 @@ export interface ListRouterParams { dataSourceId: string; } interface ListProps extends RouteComponentProps { - dataSourceEnabled: boolean; setActionMenu: (menuMount: MountPoint | undefined) => void; } interface ListState { @@ -133,6 +132,8 @@ export const DetectorList = (props: ListProps) => { (state: AppState) => state.ad.requesting ); + const dataSourceEnabled = getDataSourcePlugin().dataSourceEnabled; + const [selectedDetectors, setSelectedDetectors] = useState( [] as DetectorListItem[] ); @@ -165,7 +166,7 @@ export const DetectorList = (props: ListProps) => { // Getting all initial monitors useEffect(() => { const getInitialMonitors = async () => { - if (props.dataSourceEnabled ? state.selectedDataSourceId : true) { + if (dataSourceEnabled ? state.selectedDataSourceId : true) { dispatch(searchMonitors(state.selectedDataSourceId)); } }; @@ -240,7 +241,7 @@ export const DetectorList = (props: ListProps) => { setIsLoadingFinalDetectors(true); - if (props.dataSourceEnabled ? state.selectedDataSourceId : true) { + if (dataSourceEnabled ? state.selectedDataSourceId : true) { getUpdatedDetectors(); } }, [ @@ -496,7 +497,7 @@ export const DetectorList = (props: ListProps) => { ); }) .finally(() => { - if (props.dataSourceEnabled ? state.selectedDataSourceId : true) { + if (dataSourceEnabled ? state.selectedDataSourceId : true) { getUpdatedDetectors(); } }); @@ -690,7 +691,7 @@ export const DetectorList = (props: ListProps) => { return ( - {props.dataSourceEnabled && renderDataSourceComponent} + {dataSourceEnabled && renderDataSourceComponent} void; } @@ -83,11 +82,10 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { const visibleSampleIndices = useSelector( (state: AppState) => state.opensearch.indices ); - const allSampleDetectors = Object.values( useSelector((state: AppState) => state.ad.detectorList) ); - + const dataSourceEnabled = getDataSourcePlugin().dataSourceEnabled; const [isLoadingHttpData, setIsLoadingHttpData] = useState(false); const [isLoadingEcommerceData, setIsLoadingEcommerceData] = useState(false); @@ -122,7 +120,7 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { search: queryString.stringify(updatedParams), }) - if (props.dataSourceEnabled ? MDSOverviewState.selectedDataSourceId : true) { + if (dataSourceEnabled ? MDSOverviewState.selectedDataSourceId : true) { fetchData(); } }, [MDSOverviewState]); @@ -241,7 +239,7 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { ) : ( - {props.dataSourceEnabled && renderDataSourceComponent} + {dataSourceEnabled && renderDataSourceComponent} diff --git a/public/pages/main/Main.tsx b/public/pages/main/Main.tsx index 316aed80..1167acc8 100644 --- a/public/pages/main/Main.tsx +++ b/public/pages/main/Main.tsx @@ -34,12 +34,11 @@ enum Navigation { } interface MainProps extends RouteComponentProps { - dataSourceEnabled: boolean; setHeaderActionMenu: (menuMount: MountPoint | undefined) => void; } export function Main(props: MainProps) { - const { dataSourceEnabled, setHeaderActionMenu } = props; + const { setHeaderActionMenu } = props; const hideSideNavBar = useSelector( (state: AppState) => state.adApp.hideSideNavBar @@ -96,7 +95,6 @@ export function Main(props: MainProps) { path={APP_PATH.DASHBOARD} render={(props: RouteComponentProps) => } @@ -106,7 +104,6 @@ export function Main(props: MainProps) { path={APP_PATH.LIST_DETECTORS} render={(props: RouteComponentProps) => ( @@ -137,7 +134,6 @@ export function Main(props: MainProps) { path={APP_PATH.DETECTOR_DETAIL} render={(props: RouteComponentProps) => ( )} @@ -153,7 +149,6 @@ export function Main(props: MainProps) { path={APP_PATH.OVERVIEW} render={(props: RouteComponentProps) => ( @@ -164,13 +159,11 @@ export function Main(props: MainProps) { render={(props: RouteComponentProps) => totalDetectors > 0 ? ( ) : ( diff --git a/public/plugin.ts b/public/plugin.ts index c7d7572f..d44a9f91 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -37,6 +37,7 @@ import { setQueryService, setSavedObjectsClient, setDataSourceManagementPlugin, + setDataSourcePlugin, } from './services'; import { AnomalyDetectionOpenSearchDashboardsPluginStart } from 'public'; import { @@ -46,6 +47,7 @@ import { import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; import { DataPublicPluginStart } from '../../../src/plugins/data/public'; import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public'; +import { DataSourcePluginSetup } from '../../../src/plugins/data_source/public'; declare module '../../../src/plugins/ui_actions/public' { export interface ActionContextMapping { @@ -59,7 +61,7 @@ export interface AnomalyDetectionSetupDeps { notifications: NotificationsSetup; visAugmenter: VisAugmenterSetup; dataSourceManagement: DataSourceManagementPluginSetup; - //uiActions: UiActionsSetup; + dataSource: DataSourcePluginSetup; } export interface AnomalyDetectionStartDeps { @@ -89,7 +91,6 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin return renderApp( coreStart, params, - plugins.dataSource ); }, }); @@ -102,6 +103,8 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin setDataSourceManagementPlugin(plugins.dataSourceManagement); + setDataSourcePlugin(plugins.dataSource); + // Create context menu actions const actions = getActions(); diff --git a/public/services.ts b/public/services.ts index f79e8cc3..705d4d3e 100644 --- a/public/services.ts +++ b/public/services.ts @@ -15,6 +15,7 @@ import { EmbeddableStart } from '../../../src/plugins/embeddable/public'; import { createGetterSetter } from '../../../src/plugins/opensearch_dashboards_utils/public'; import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; import { SavedAugmentVisLoader } from '../../../src/plugins/vis_augmenter/public'; +import { DataSourcePluginSetup } from '../../../src/plugins/data_source/public'; export const [getSavedFeatureAnywhereLoader, setSavedFeatureAnywhereLoader] = createGetterSetter('savedFeatureAnywhereLoader'); @@ -46,6 +47,9 @@ export const [getSavedObjectsClient, setSavedObjectsClient] = export const [getDataSourceManagementPlugin, setDataSourceManagementPlugin] = createGetterSetter('DataSourceManagement'); +export const [getDataSourcePlugin, setDataSourcePlugin] = + createGetterSetter('DataSource'); + // This is primarily used for mocking this module and each of its fns in tests. export default { getSavedFeatureAnywhereLoader, From f166bd2463d5d314a6138ee082c8a72c92e26231 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Tue, 16 Apr 2024 21:46:29 -0700 Subject: [PATCH 21/26] read dataSourceId from the url instead of passing props Signed-off-by: Jackie Han --- public/models/interfaces.ts | 2 - .../containers/AnomaliesChart.tsx | 3 - .../containers/AnomalyDetailsChart.tsx | 12 +- .../containers/AnomalyOccurrenceChart.tsx | 2 - .../Components/AnomaliesDistribution.tsx | 7 +- .../Components/AnomaliesLiveChart.tsx | 10 +- .../Dashboard/Container/DashboardOverview.tsx | 4 +- .../containers/DetectorConfig.tsx | 8 +- .../containers/DetectorDetail.tsx | 13 +- .../containers/AnomalyHistory.tsx | 9 +- .../containers/AnomalyResults.tsx | 17 +- .../containers/AnomalyResultsLiveChart.tsx | 6 +- .../DetectorsList/containers/List/List.tsx | 7 +- .../pages/DetectorsList/utils/tableUtils.tsx | 242 +++++++++--------- .../containers/HistoricalDetectorResults.tsx | 8 +- .../SampleDataBox/SampleDataBox.tsx | 8 +- .../containers/AnomalyDetectionOverview.tsx | 3 - public/pages/utils/helpers.ts | 4 - public/utils/constants.ts | 2 + 19 files changed, 180 insertions(+), 187 deletions(-) diff --git a/public/models/interfaces.ts b/public/models/interfaces.ts index ff578f12..eff5ead5 100644 --- a/public/models/interfaces.ts +++ b/public/models/interfaces.ts @@ -205,7 +205,6 @@ export type Detector = { taskState?: DETECTOR_STATE; taskProgress?: number; taskError?: string; - dataSourceId? : string; }; export type DetectorListItem = { @@ -219,7 +218,6 @@ export type DetectorListItem = { lastUpdateTime: number; enabledTime?: number; detectorType?: string; - dataSourceId?: string; }; export type EntityData = { diff --git a/public/pages/AnomalyCharts/containers/AnomaliesChart.tsx b/public/pages/AnomalyCharts/containers/AnomaliesChart.tsx index 67d0a004..49f97213 100644 --- a/public/pages/AnomalyCharts/containers/AnomaliesChart.tsx +++ b/public/pages/AnomalyCharts/containers/AnomaliesChart.tsx @@ -85,7 +85,6 @@ export interface AnomaliesChartProps { selectedCategoryFields?: any[]; handleCategoryFieldsChange(selectedOptions: any[]): void; openOutOfRangeCallOut?: boolean; - dataSourceId?: string; } export const AnomaliesChart = React.memo((props: AnomaliesChartProps) => { @@ -346,7 +345,6 @@ export const AnomaliesChart = React.memo((props: AnomaliesChartProps) => { isHCDetector={props.isHCDetector} isHistorical={props.isHistorical} selectedHeatmapCell={props.selectedHeatmapCell} - dataSourceId={props.dataSourceId} />, , { isHistorical={props.isHistorical} onDatePickerRangeChange={handleDatePickerRangeChange} openOutOfRangeCallOut={showOutOfRangeCallOut} - dataSourceId={props.dataSourceId} /> )} diff --git a/public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx b/public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx index 8ee7b37b..81ee473b 100644 --- a/public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx +++ b/public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx @@ -81,7 +81,7 @@ import { } from '../utils/constants'; import { HeatmapCell } from './AnomalyHeatmapChart'; import { ANOMALY_AGG, MIN_END_TIME, MAX_END_TIME } from '../../utils/constants'; -import { MAX_HISTORICAL_AGG_RESULTS } from '../../../utils/constants'; +import { DATA_SOURCE_ID, MAX_HISTORICAL_AGG_RESULTS } from '../../../utils/constants'; import { searchResults } from '../../../redux/reducers/anomalyResults'; import { DAY_IN_MILLI_SECS, @@ -89,6 +89,7 @@ import { DETECTOR_STATE, } from '../../../../server/utils/constants'; import { ENTITY_COLORS } from '../../DetectorResults/utils/constants'; +import { useLocation } from 'react-router-dom'; interface AnomalyDetailsChartProps { onDateRangeChange( @@ -113,12 +114,13 @@ interface AnomalyDetailsChartProps { selectedHeatmapCell?: HeatmapCell; onDatePickerRangeChange?(startDate: number, endDate: number): void; openOutOfRangeCallOut?: boolean; - dataSourceId?: string; } export const AnomalyDetailsChart = React.memo( (props: AnomalyDetailsChartProps) => { const dispatch = useDispatch(); + const location = useLocation(); + const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; const [showAlertsFlyout, setShowAlertsFlyout] = useState(false); const [alertAnnotations, setAlertAnnotations] = useState([]); const [isLoadingAlerts, setIsLoadingAlerts] = useState(false); @@ -175,7 +177,7 @@ export const AnomalyDetailsChart = React.memo( zoomRange.endDate, taskId ); - dispatch(searchResults(anomalyDataRangeQuery, resultIndex, props.dataSourceId, true)) + dispatch(searchResults(anomalyDataRangeQuery, resultIndex, dataSourceId, true)) .then((response: any) => { // Only retrieve buckets that are in the anomaly results range. This is so // we don't show aggregate results for where there is no data at all @@ -194,7 +196,7 @@ export const AnomalyDetailsChart = React.memo( taskId, selectedAggId ); - dispatch(searchResults(historicalAggQuery, resultIndex, props.dataSourceId, true)) + dispatch(searchResults(historicalAggQuery, resultIndex, dataSourceId, true)) .then((response: any) => { const aggregatedAnomalies = parseHistoricalAggregatedAnomalies( response, @@ -230,7 +232,7 @@ export const AnomalyDetailsChart = React.memo( zoomRange.endDate, taskId ); - dispatch(searchResults(anomalyDataRangeQuery, resultIndex, props.dataSourceId, true)) + dispatch(searchResults(anomalyDataRangeQuery, resultIndex, dataSourceId, true)) .then((response: any) => { const dataStartDate = get( response, diff --git a/public/pages/AnomalyCharts/containers/AnomalyOccurrenceChart.tsx b/public/pages/AnomalyCharts/containers/AnomalyOccurrenceChart.tsx index 92823f23..0bb8d9df 100644 --- a/public/pages/AnomalyCharts/containers/AnomalyOccurrenceChart.tsx +++ b/public/pages/AnomalyCharts/containers/AnomalyOccurrenceChart.tsx @@ -44,7 +44,6 @@ interface AnomalyOccurrenceChartProps { isHCDetector?: boolean; isHistorical?: boolean; selectedHeatmapCell?: HeatmapCell; - dataSourceId?: string; } export const AnomalyOccurrenceChart = React.memo( @@ -83,7 +82,6 @@ export const AnomalyOccurrenceChart = React.memo( isHCDetector={props.isHCDetector} isHistorical={props.isHistorical} selectedHeatmapCell={props.selectedHeatmapCell} - dataSourceId={props.dataSourceId} /> {props.isHCDetector && props.selectedHeatmapCell === undefined ? ( diff --git a/public/pages/Dashboard/Components/AnomaliesDistribution.tsx b/public/pages/Dashboard/Components/AnomaliesDistribution.tsx index 06b27eab..74c68fc2 100644 --- a/public/pages/Dashboard/Components/AnomaliesDistribution.tsx +++ b/public/pages/Dashboard/Components/AnomaliesDistribution.tsx @@ -33,15 +33,18 @@ import { get, isEmpty } from 'lodash'; import { AD_DOC_FIELDS } from '../../../../server/utils/constants'; import { ALL_CUSTOM_AD_RESULT_INDICES } from '../../utils/constants'; import { searchResults } from '../../../redux/reducers/anomalyResults'; +import { useLocation } from 'react-router-dom'; +import { DATA_SOURCE_ID } from '../../../utils/constants'; export interface AnomaliesDistributionChartProps { selectedDetectors: DetectorListItem[]; - dataSourceId?: string; } export const AnomaliesDistributionChart = ( props: AnomaliesDistributionChartProps ) => { const dispatch = useDispatch(); + const location = useLocation(); + const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; const [anomalyDistribution, setAnomalyDistribution] = useState( [] as object[] @@ -67,7 +70,7 @@ export const AnomaliesDistributionChart = ( await getAnomalyDistributionForDetectorsByTimeRange( searchResults, props.selectedDetectors, - props.dataSourceId, + dataSourceId, timeRange, dispatch, 0, diff --git a/public/pages/Dashboard/Components/AnomaliesLiveChart.tsx b/public/pages/Dashboard/Components/AnomaliesLiveChart.tsx index 005c0773..21e728a9 100644 --- a/public/pages/Dashboard/Components/AnomaliesLiveChart.tsx +++ b/public/pages/Dashboard/Components/AnomaliesLiveChart.tsx @@ -51,13 +51,13 @@ import { getLatestAnomalyResultsForDetectorsByTimeRange, getLatestAnomalyResultsByTimeRange, } from '../utils/utils'; -import { MAX_ANOMALIES, SPACE_STR } from '../../../utils/constants'; +import { DATA_SOURCE_ID, MAX_ANOMALIES, SPACE_STR } from '../../../utils/constants'; import { ALL_CUSTOM_AD_RESULT_INDICES } from '../../utils/constants'; import { searchResults } from '../../../redux/reducers/anomalyResults'; +import { useLocation } from 'react-router-dom'; export interface AnomaliesLiveChartProps { selectedDetectors: DetectorListItem[]; - dataSourceId?: string; } interface LiveTimeRangeState { @@ -69,6 +69,8 @@ const MAX_LIVE_DETECTORS = 10; export const AnomaliesLiveChart = (props: AnomaliesLiveChartProps) => { const dispatch = useDispatch(); + const location = useLocation(); + const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; const [liveTimeRange, setLiveTimeRange] = useState({ startDateTime: moment().subtract(31, 'minutes'), @@ -104,7 +106,7 @@ export const AnomaliesLiveChart = (props: AnomaliesLiveChartProps) => { true, ALL_CUSTOM_AD_RESULT_INDICES, false, - props.dataSourceId + dataSourceId ); } catch (err) { console.log( @@ -129,7 +131,7 @@ export const AnomaliesLiveChart = (props: AnomaliesLiveChartProps) => { false, ALL_CUSTOM_AD_RESULT_INDICES, false, - props.dataSourceId + dataSourceId ); setLiveAnomalyData(latestLiveAnomalyResult); diff --git a/public/pages/Dashboard/Container/DashboardOverview.tsx b/public/pages/Dashboard/Container/DashboardOverview.tsx index 3febf2fe..943cf7b4 100644 --- a/public/pages/Dashboard/Container/DashboardOverview.tsx +++ b/public/pages/Dashboard/Container/DashboardOverview.tsx @@ -313,14 +313,12 @@ export function DashboardOverview(props: OverviewProps) { + selectedDetectors={currentDetectors} /> diff --git a/public/pages/DetectorConfig/containers/DetectorConfig.tsx b/public/pages/DetectorConfig/containers/DetectorConfig.tsx index 6cd37104..0539c5a9 100644 --- a/public/pages/DetectorConfig/containers/DetectorConfig.tsx +++ b/public/pages/DetectorConfig/containers/DetectorConfig.tsx @@ -14,26 +14,28 @@ import { DetectorDefinitionFields } from '../../ReviewAndCreate/components/Detec import { Features } from './Features'; import { DetectorJobs } from './DetectorJobs'; import { EuiSpacer, EuiPage, EuiPageBody } from '@elastic/eui'; -import { RouteComponentProps } from 'react-router'; +import { RouteComponentProps, useLocation } from 'react-router'; import { AppState } from '../../../redux/reducers'; import { useSelector, useDispatch } from 'react-redux'; import { getDetector } from '../../../redux/reducers/ad'; import { EuiLoadingSpinner } from '@elastic/eui'; +import { DATA_SOURCE_ID } from '../../../utils/constants'; interface DetectorConfigProps extends RouteComponentProps { detectorId: string; - dataSourceId: string; onEditFeatures(): void; onEditDetector(): void; } export function DetectorConfig(props: DetectorConfigProps) { const dispatch = useDispatch(); + const location = useLocation(); + const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; const detector = useSelector( (state: AppState) => state.ad.detectors[props.detectorId] ); useEffect(() => { - dispatch(getDetector(props.detectorId, props.dataSourceId)); + dispatch(getDetector(props.detectorId, dataSourceId)); }, []); return ( diff --git a/public/pages/DetectorDetail/containers/DetectorDetail.tsx b/public/pages/DetectorDetail/containers/DetectorDetail.tsx index b70206a8..6fd7f10d 100644 --- a/public/pages/DetectorDetail/containers/DetectorDetail.tsx +++ b/public/pages/DetectorDetail/containers/DetectorDetail.tsx @@ -42,7 +42,7 @@ import { import { getIndices } from '../../../redux/reducers/opensearch'; import { getErrorMessage, Listener } from '../../../utils/utils'; import { darkModeEnabled } from '../../../utils/opensearchDashboardsUtils'; -import { BREADCRUMBS } from '../../../utils/constants'; +import { BREADCRUMBS, DATA_SOURCE_ID } from '../../../utils/constants'; import { DetectorControls } from '../components/DetectorControls'; import { ConfirmModal } from '../components/ConfirmModal/ConfirmModal'; import { useFetchMonitorInfo } from '../hooks/useFetchMonitorInfo'; @@ -108,8 +108,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { const detectorId = get(props, 'match.params.detectorId', '') as string; const dataSourceEnabled = getDataSourcePlugin().dataSourceEnabled; const location = useLocation(); - const queryParams = new URLSearchParams(location.search); - const dataSourceId = queryParams.get('dataSourceId') as string; + const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; const { detector, hasError, isLoadingDetector, errorMessage } = useFetchDetectorInfo(detectorId, dataSourceId); @@ -162,7 +161,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { // Getting all visible indices. Will re-fetch if changes to the detector (e.g., // detector starts, result index recreated or user switches tabs to re-fetch detector) useEffect(() => { - if (props.dataSourceEnabled ? dataSourceId : true) { + if (dataSourceEnabled ? dataSourceId : true) { const getInitialIndices = async () => { await dispatch(getIndices('', dataSourceId)).catch((error: any) => { console.error(error); @@ -373,7 +372,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { }} > - {props.dataSourceEnabled && ( + {dataSourceEnabled && ( { handleStartAdJob(detectorId)} onStopDetector={() => handleStopAdJob(detectorId)} onSwitchToConfiguration={handleSwitchToConfigurationTab} @@ -582,7 +579,6 @@ export const DetectorDetail = (props: DetectorDetailProps) => { )} /> @@ -593,7 +589,6 @@ export const DetectorDetail = (props: DetectorDetailProps) => { diff --git a/public/pages/DetectorResults/containers/AnomalyHistory.tsx b/public/pages/DetectorResults/containers/AnomalyHistory.tsx index 6eaff6c4..a105d98d 100644 --- a/public/pages/DetectorResults/containers/AnomalyHistory.tsx +++ b/public/pages/DetectorResults/containers/AnomalyHistory.tsx @@ -65,7 +65,7 @@ import { TOP_CHILD_ENTITIES_TO_FETCH, } from '../utils/constants'; import { MIN_IN_MILLI_SECS } from '../../../../server/utils/constants'; -import { MAX_ANOMALIES } from '../../../utils/constants'; +import { DATA_SOURCE_ID, MAX_ANOMALIES } from '../../../utils/constants'; import { searchResults, getDetectorResults, @@ -95,10 +95,10 @@ import { import { CoreStart } from '../../../../../../src/core/public'; import { CoreServicesContext } from '../../../components/CoreServices/CoreServices'; import { prettifyErrorMessage } from '../../../../server/utils/helpers'; +import { useLocation } from 'react-router-dom'; interface AnomalyHistoryProps { detector: Detector; - dataSourceId: string; monitor: Monitor | undefined; isFeatureDataMissing?: boolean; isHistorical?: boolean; @@ -127,7 +127,8 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { props.isHistorical && props.detector?.detectionDateRange ? props.detector.detectionDateRange.endTime : moment().valueOf(); - const dataSourceId = props.dataSourceId; + const location = useLocation(); + const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; const [dateRange, setDateRange] = useState({ startDate: initialStartDate, endDate: initialEndDate, @@ -876,7 +877,6 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { entityAnomalySummaries={entityAnomalySummaries} selectedCategoryFields={selectedCategoryFields} handleCategoryFieldsChange={handleCategoryFieldsChange} - dataSourceId={dataSourceId} >
    {isHCDetector @@ -920,7 +920,6 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { isHCDetector={isHCDetector} isHistorical={props.isHistorical} selectedHeatmapCell={selectedHeatmapCell} - dataSourceId={dataSourceId} />, , ] diff --git a/public/pages/DetectorResults/containers/AnomalyResults.tsx b/public/pages/DetectorResults/containers/AnomalyResults.tsx index 419d3a3e..377bfbb6 100644 --- a/public/pages/DetectorResults/containers/AnomalyResults.tsx +++ b/public/pages/DetectorResults/containers/AnomalyResults.tsx @@ -28,10 +28,11 @@ import { import { get, isEmpty } from 'lodash'; import React, { useEffect, Fragment, useState } from 'react'; import { useSelector, useDispatch } from 'react-redux'; -import { RouteComponentProps } from 'react-router'; +import { RouteComponentProps, useLocation } from 'react-router'; import { AppState } from '../../../redux/reducers'; import { BREADCRUMBS, + DATA_SOURCE_ID, FEATURE_DATA_POINTS_WINDOW, MISSING_FEATURE_DATA_SEVERITY, } from '../../../utils/constants'; @@ -67,11 +68,11 @@ import { SampleIndexDetailsCallout } from '../../Overview/components/SampleIndex import { CoreStart } from '../../../../../../src/core/public'; import { CoreServicesContext } from '../../../components/CoreServices/CoreServices'; import { DEFAULT_SHINGLE_SIZE } from '../../../utils/constants'; +import { getDataSourcePlugin } from '../../../../public/services'; + interface AnomalyResultsProps extends RouteComponentProps { detectorId: string; - dataSourceId: string; - dataSourceEnabled: boolean; onStartDetector(): void; onStopDetector(): void; onSwitchToConfiguration(): void; @@ -82,10 +83,12 @@ export function AnomalyResults(props: AnomalyResultsProps) { const core = React.useContext(CoreServicesContext) as CoreStart; const dispatch = useDispatch(); const detectorId = props.detectorId; - const dataSourceId = props.dataSourceId; const detector = useSelector( (state: AppState) => state.ad.detectors[detectorId] ); + const dataSourceEnabled = getDataSourcePlugin().dataSourceEnabled; + const location = useLocation(); + const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; useEffect(() => { core.chrome.setBreadcrumbs([ @@ -93,13 +96,13 @@ export function AnomalyResults(props: AnomalyResultsProps) { BREADCRUMBS.DETECTORS, { text: detector ? detector.name : '' }, ]); - if (props.dataSourceEnabled ? dataSourceId : true) { + if (dataSourceEnabled ? dataSourceId : true) { dispatch(getDetector(detectorId, dataSourceId)); } }, []); const fetchDetector = async () => { - if (props.dataSourceEnabled ? dataSourceId : true) { + if (dataSourceEnabled ? dataSourceId : true) { dispatch(getDetector(detectorId, dataSourceId)); } }; @@ -559,12 +562,10 @@ export function AnomalyResults(props: AnomalyResultsProps) { ) : null} { const dispatch = useDispatch(); + const location = useLocation(); + const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; const [firstLoading, setFirstLoading] = useState(true); const [isFullScreen, setIsFullScreen] = useState(false); @@ -81,7 +84,6 @@ export const AnomalyResultsLiveChart = ( 'detectionInterval.period.interval', 1 ); - const dataSourceId = props.dataSourceId; const startDateTime = moment().subtract( detectionInterval * LIVE_CHART_CONFIG.MONITORING_INTERVALS, 'minutes' diff --git a/public/pages/DetectorsList/containers/List/List.tsx b/public/pages/DetectorsList/containers/List/List.tsx index d93c3230..94ad679d 100644 --- a/public/pages/DetectorsList/containers/List/List.tsx +++ b/public/pages/DetectorsList/containers/List/List.tsx @@ -68,7 +68,7 @@ import { filterAndSortDetectors, getDetectorsToDisplay, } from '../../../utils/helpers'; -import { staticColumn } from '../../utils/tableUtils'; +import { getColumns } from '../../utils/tableUtils'; import { DETECTOR_ACTION } from '../../utils/constants'; import { getTitleWithCount, Listener } from '../../../../utils/utils'; import { ListActions } from '../../components/ListActions/ListActions'; @@ -268,7 +268,6 @@ export const DetectorList = (props: ListProps) => { curSelectedDetectors, state.page, state.queryParams.size, - state.selectedDataSourceId ); setDetectorsToDisplay(curDetectorsToDisplay); @@ -688,6 +687,8 @@ export const DetectorList = (props: ListProps) => { ); }, [getSavedObjectsClient(), getNotifications(), props.setActionMenu]); + const columns = getColumns(state.selectedDataSourceId); + return ( @@ -749,7 +750,7 @@ export const DetectorList = (props: ListProps) => { monitors list page. */ itemId={getItemId} - columns={staticColumn} + columns={columns} onChange={handleTableChange} isSelectable={true} selection={selection} diff --git a/public/pages/DetectorsList/utils/tableUtils.tsx b/public/pages/DetectorsList/utils/tableUtils.tsx index f0ddf617..ba3e5f1d 100644 --- a/public/pages/DetectorsList/utils/tableUtils.tsx +++ b/public/pages/DetectorsList/utils/tableUtils.tsx @@ -51,128 +51,126 @@ export const renderState = (state: DETECTOR_STATE) => { ); }; -export const staticColumn = [ - { - field: 'name', - name: ( - - Detector{''} - - ), - sortable: true, - truncateText: true, - textOnly: true, - align: 'left', - width: '15%', - render: (name: string, detector: Detector) => { - let href = `${PLUGIN_NAME}#/detectors/${detector.id}/results`; - if (detector.dataSourceId) { - href += `?dataSourceId=${detector.dataSourceId}`; - } - return ( - - {name} - - ); +export function getColumns(dataSourceId) { + return [ + { + field: 'name', + name: ( + + Detector{''} + + ), + sortable: true, + truncateText: true, + textOnly: true, + align: 'left', + width: '15%', + render: (name: string, detector: Detector) => { + let href = `${PLUGIN_NAME}#/detectors/${detector.id}/results`; + if (dataSourceId) { + href += `?dataSourceId=${dataSourceId}`; + } + return {name}; + }, + }, + { + field: 'indices', + name: ( + + Indices{''} + + ), + sortable: true, + truncateText: true, + textOnly: true, + align: 'left', + width: '15%', + render: renderIndices, + }, + { + field: 'curState', + name: ( + + Real-time state{''} + + ), + sortable: true, + dataType: 'string', + align: 'left', + width: '12%', + truncateText: false, + render: renderState, }, - }, - { - field: 'indices', - name: ( - - Indices{''} - - ), - sortable: true, - truncateText: true, - textOnly: true, - align: 'left', - width: '15%', - render: renderIndices, - }, - { - field: 'curState', - name: ( - - Real-time state{''} - - ), - sortable: true, - dataType: 'string', - align: 'left', - width: '12%', - truncateText: false, - render: renderState, - }, - { - field: 'task', - name: ( - - Historical analysis{''} - - ), - sortable: true, - truncateText: true, - textOnly: true, - align: 'left', - width: '15%', - render: (name: string, detector: Detector) => { - if (!isEmpty(detector.taskId)) { - let href = `${PLUGIN_NAME}#/detectors/${detector.id}/historical`; - if (detector.dataSourceId) { - href += `?dataSourceId=${detector.dataSourceId}`; + { + field: 'task', + name: ( + + Historical analysis{''} + + ), + sortable: true, + truncateText: true, + textOnly: true, + align: 'left', + width: '15%', + render: (name: string, detector: Detector) => { + if (!isEmpty(detector.taskId)) { + let href = `${PLUGIN_NAME}#/detectors/${detector.id}/historical`; + if (dataSourceId) { + href += `?dataSourceId=${dataSourceId}`; + } + return ( + + View results + + ); + } else { + return ( + - + ); } - return ( - - View results - - ); - } else { - return ( - - - ); - } + }, + }, + { + field: 'totalAnomalies', + name: ( + + Anomalies last 24 hours{''} + + ), + sortable: true, + dataType: 'number', + align: 'right', + width: '16%', + truncateText: false, + }, + { + field: 'lastActiveAnomaly', + name: ( + + Last real-time occurrence{''} + + ), + sortable: true, + dataType: 'date', + truncateText: false, + align: 'left', + width: '16%', + render: renderTime, + }, + { + field: 'enabledTime', + name: ( + + Last started{''} + + ), + sortable: true, + dataType: 'date', + truncateText: false, + align: 'left', + width: '16%', + render: renderTime, }, - }, - { - field: 'totalAnomalies', - name: ( - - Anomalies last 24 hours{''} - - ), - sortable: true, - dataType: 'number', - align: 'right', - width: '16%', - truncateText: false, - }, - { - field: 'lastActiveAnomaly', - name: ( - - Last real-time occurrence{''} - - ), - sortable: true, - dataType: 'date', - truncateText: false, - align: 'left', - width: '16%', - render: renderTime, - }, - { - field: 'enabledTime', - name: ( - - Last started{''} - - ), - sortable: true, - dataType: 'date', - truncateText: false, - align: 'left', - width: '16%', - render: renderTime, - }, -] as EuiBasicTableColumn[]; + ]as EuiBasicTableColumn[]; +} diff --git a/public/pages/HistoricalDetectorResults/containers/HistoricalDetectorResults.tsx b/public/pages/HistoricalDetectorResults/containers/HistoricalDetectorResults.tsx index 879a153e..adbadc0c 100644 --- a/public/pages/HistoricalDetectorResults/containers/HistoricalDetectorResults.tsx +++ b/public/pages/HistoricalDetectorResults/containers/HistoricalDetectorResults.tsx @@ -23,7 +23,7 @@ import { } from '@elastic/eui'; import { get, isEmpty } from 'lodash'; import React, { useEffect, Fragment, useState } from 'react'; -import { RouteComponentProps } from 'react-router'; +import { RouteComponentProps, useLocation } from 'react-router'; import { useSelector, useDispatch } from 'react-redux'; import { darkModeEnabled } from '../../../utils/opensearchDashboardsUtils'; import { AppState } from '../../../redux/reducers'; @@ -50,10 +50,10 @@ import { import { prettifyErrorMessage } from '../../../../server/utils/helpers'; import { EmptyHistoricalDetectorResults } from '../components/EmptyHistoricalDetectorResults'; import { HistoricalDetectorCallout } from '../components/HistoricalDetectorCallout'; +import { DATA_SOURCE_ID } from '../../../utils/constants'; interface HistoricalDetectorResultsProps extends RouteComponentProps { detectorId: string; - dataSourceId: string; } export function HistoricalDetectorResults( @@ -63,7 +63,8 @@ export function HistoricalDetectorResults( const isDark = darkModeEnabled(); const dispatch = useDispatch(); const detectorId: string = get(props, 'match.params.detectorId', ''); - const dataSourceId = props.dataSourceId; + const location = useLocation(); + const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; const adState = useSelector((state: AppState) => state.ad); const allDetectors = adState.detectors; @@ -256,7 +257,6 @@ export function HistoricalDetectorResults( { + const location = useLocation(); + const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; return (
    { {props.isDataLoaded ? ( View detector and sample data diff --git a/public/pages/Overview/containers/AnomalyDetectionOverview.tsx b/public/pages/Overview/containers/AnomalyDetectionOverview.tsx index 2966f4f4..0e8645b9 100644 --- a/public/pages/Overview/containers/AnomalyDetectionOverview.tsx +++ b/public/pages/Overview/containers/AnomalyDetectionOverview.tsx @@ -332,7 +332,6 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { sampleHttpResponses.detectorName, sampleHttpResponses.legacyDetectorName )} - dataSourceId={MDSOverviewState.selectedDataSourceId} /> @@ -365,7 +364,6 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { sampleEcommerce.detectorName, sampleEcommerce.legacyDetectorName )} - dataSourceId={MDSOverviewState.selectedDataSourceId} /> @@ -400,7 +398,6 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { sampleHostHealth.detectorName, sampleHostHealth.legacyDetectorName )} - dataSourceId={MDSOverviewState.selectedDataSourceId} /> diff --git a/public/pages/utils/helpers.ts b/public/pages/utils/helpers.ts index 2d38d497..c0126021 100644 --- a/public/pages/utils/helpers.ts +++ b/public/pages/utils/helpers.ts @@ -97,11 +97,7 @@ export const getDetectorsToDisplay = ( detectors: DetectorListItem[], page: number, size: number, - dataSourceId: string ) => { - detectors.forEach((detector) => { - detector.dataSourceId = dataSourceId; - }); return detectors.slice(size * page, page * size + size); }; diff --git a/public/utils/constants.ts b/public/utils/constants.ts index 6f244704..99988470 100644 --- a/public/utils/constants.ts +++ b/public/utils/constants.ts @@ -98,3 +98,5 @@ export enum MISSING_FEATURE_DATA_SEVERITY { export const SPACE_STR = ' '; export const ANOMALY_DETECTION_ICON = 'anomalyDetection'; + +export const DATA_SOURCE_ID = 'dataSourceId'; From 67f0a084a8f324a13cd322057d69714745b8a87b Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Wed, 17 Apr 2024 15:14:00 -0700 Subject: [PATCH 22/26] addressing comments and run prettier Signed-off-by: Jackie Han --- public/anomaly_detection_app.tsx | 5 +- .../containers/AnomalyDetailsChart.tsx | 20 +- .../Components/AnomaliesDistribution.tsx | 3 +- .../Components/AnomaliesLiveChart.tsx | 9 +- .../Dashboard/Container/DashboardOverview.tsx | 89 +++-- .../containers/DetectorConfig.tsx | 3 +- .../containers/DetectorDetail.tsx | 92 +++-- .../hooks/useFetchMonitorInfo.ts | 4 +- .../containers/AnomalyHistory.tsx | 39 ++- .../containers/AnomalyResults.tsx | 31 +- .../containers/AnomalyResultsLiveChart.tsx | 5 +- .../DetectorsList/containers/List/List.tsx | 68 ++-- public/pages/DetectorsList/utils/helpers.ts | 11 +- .../pages/DetectorsList/utils/tableUtils.tsx | 14 +- .../containers/HistoricalDetectorResults.tsx | 12 +- .../SampleDataBox/SampleDataBox.tsx | 3 +- .../containers/AnomalyDetectionOverview.tsx | 107 +++--- public/pages/main/Main.tsx | 60 +++- public/pages/utils/helpers.ts | 9 +- public/plugin.ts | 5 +- public/redux/reducers/ad.ts | 91 +++-- public/redux/reducers/alerting.ts | 4 +- public/redux/reducers/anomalyResults.ts | 36 +- public/redux/reducers/liveAnomalyResults.ts | 14 +- public/redux/reducers/opensearch.ts | 21 +- public/redux/reducers/sampleData.ts | 14 +- public/services.ts | 4 +- server/models/types.ts | 2 +- server/routes/ad.ts | 319 ++++++++++-------- server/routes/alerting.ts | 22 +- server/routes/opensearch.ts | 78 ++--- server/routes/sampleData.ts | 21 +- server/sampleData/utils/helpers.ts | 16 +- server/utils/helpers.ts | 9 +- 34 files changed, 742 insertions(+), 498 deletions(-) diff --git a/public/anomaly_detection_app.tsx b/public/anomaly_detection_app.tsx index 37e2e5f4..326cf18b 100644 --- a/public/anomaly_detection_app.tsx +++ b/public/anomaly_detection_app.tsx @@ -18,10 +18,7 @@ import { Provider } from 'react-redux'; import configureStore from './redux/configureStore'; import { CoreServicesContext } from './components/CoreServices/CoreServices'; -export function renderApp( - coreStart: CoreStart, - params: AppMountParameters, -) { +export function renderApp(coreStart: CoreStart, params: AppMountParameters) { const http = coreStart.http; const store = configureStore(http); diff --git a/public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx b/public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx index 81ee473b..96aa189e 100644 --- a/public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx +++ b/public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx @@ -81,7 +81,10 @@ import { } from '../utils/constants'; import { HeatmapCell } from './AnomalyHeatmapChart'; import { ANOMALY_AGG, MIN_END_TIME, MAX_END_TIME } from '../../utils/constants'; -import { DATA_SOURCE_ID, MAX_HISTORICAL_AGG_RESULTS } from '../../../utils/constants'; +import { + DATA_SOURCE_ID, + MAX_HISTORICAL_AGG_RESULTS, +} from '../../../utils/constants'; import { searchResults } from '../../../redux/reducers/anomalyResults'; import { DAY_IN_MILLI_SECS, @@ -120,7 +123,8 @@ export const AnomalyDetailsChart = React.memo( (props: AnomalyDetailsChartProps) => { const dispatch = useDispatch(); const location = useLocation(); - const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; + const dataSourceId = + new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; const [showAlertsFlyout, setShowAlertsFlyout] = useState(false); const [alertAnnotations, setAlertAnnotations] = useState([]); const [isLoadingAlerts, setIsLoadingAlerts] = useState(false); @@ -177,7 +181,9 @@ export const AnomalyDetailsChart = React.memo( zoomRange.endDate, taskId ); - dispatch(searchResults(anomalyDataRangeQuery, resultIndex, dataSourceId, true)) + dispatch( + searchResults(anomalyDataRangeQuery, resultIndex, dataSourceId, true) + ) .then((response: any) => { // Only retrieve buckets that are in the anomaly results range. This is so // we don't show aggregate results for where there is no data at all @@ -196,7 +202,9 @@ export const AnomalyDetailsChart = React.memo( taskId, selectedAggId ); - dispatch(searchResults(historicalAggQuery, resultIndex, dataSourceId, true)) + dispatch( + searchResults(historicalAggQuery, resultIndex, dataSourceId, true) + ) .then((response: any) => { const aggregatedAnomalies = parseHistoricalAggregatedAnomalies( response, @@ -232,7 +240,9 @@ export const AnomalyDetailsChart = React.memo( zoomRange.endDate, taskId ); - dispatch(searchResults(anomalyDataRangeQuery, resultIndex, dataSourceId, true)) + dispatch( + searchResults(anomalyDataRangeQuery, resultIndex, dataSourceId, true) + ) .then((response: any) => { const dataStartDate = get( response, diff --git a/public/pages/Dashboard/Components/AnomaliesDistribution.tsx b/public/pages/Dashboard/Components/AnomaliesDistribution.tsx index 74c68fc2..0d590adc 100644 --- a/public/pages/Dashboard/Components/AnomaliesDistribution.tsx +++ b/public/pages/Dashboard/Components/AnomaliesDistribution.tsx @@ -44,7 +44,8 @@ export const AnomaliesDistributionChart = ( ) => { const dispatch = useDispatch(); const location = useLocation(); - const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; + const dataSourceId = + new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; const [anomalyDistribution, setAnomalyDistribution] = useState( [] as object[] diff --git a/public/pages/Dashboard/Components/AnomaliesLiveChart.tsx b/public/pages/Dashboard/Components/AnomaliesLiveChart.tsx index 21e728a9..ed4490c1 100644 --- a/public/pages/Dashboard/Components/AnomaliesLiveChart.tsx +++ b/public/pages/Dashboard/Components/AnomaliesLiveChart.tsx @@ -51,7 +51,11 @@ import { getLatestAnomalyResultsForDetectorsByTimeRange, getLatestAnomalyResultsByTimeRange, } from '../utils/utils'; -import { DATA_SOURCE_ID, MAX_ANOMALIES, SPACE_STR } from '../../../utils/constants'; +import { + DATA_SOURCE_ID, + MAX_ANOMALIES, + SPACE_STR, +} from '../../../utils/constants'; import { ALL_CUSTOM_AD_RESULT_INDICES } from '../../utils/constants'; import { searchResults } from '../../../redux/reducers/anomalyResults'; import { useLocation } from 'react-router-dom'; @@ -70,7 +74,8 @@ const MAX_LIVE_DETECTORS = 10; export const AnomaliesLiveChart = (props: AnomaliesLiveChartProps) => { const dispatch = useDispatch(); const location = useLocation(); - const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; + const dataSourceId = + new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; const [liveTimeRange, setLiveTimeRange] = useState({ startDateTime: moment().subtract(31, 'minutes'), diff --git a/public/pages/Dashboard/Container/DashboardOverview.tsx b/public/pages/Dashboard/Container/DashboardOverview.tsx index 943cf7b4..46ed3307 100644 --- a/public/pages/Dashboard/Container/DashboardOverview.tsx +++ b/public/pages/Dashboard/Container/DashboardOverview.tsx @@ -35,11 +35,21 @@ import { ALL_INDICES_MESSAGE, } from '../utils/constants'; import { AppState } from '../../../redux/reducers'; -import { CatIndex, IndexAlias, MDSQueryParams } from '../../../../server/models/types'; -import { getAllDetectorsQueryParamsWithDataSourceId, getVisibleOptions } from '../../utils/helpers'; +import { + CatIndex, + IndexAlias, + MDSQueryParams, +} from '../../../../server/models/types'; +import { + getAllDetectorsQueryParamsWithDataSourceId, + getVisibleOptions, +} from '../../utils/helpers'; import { BREADCRUMBS } from '../../../utils/constants'; import { DETECTOR_STATE } from '../../../../server/utils/constants'; -import { getDetectorStateOptions, getURLQueryParams } from '../../DetectorsList/utils/helpers'; +import { + getDetectorStateOptions, + getURLQueryParams, +} from '../../DetectorsList/utils/helpers'; import { DashboardHeader } from '../Components/utils/DashboardHeader'; import { EmptyDashboard } from '../Components/EmptyDashboard/EmptyDashboard'; import { @@ -49,7 +59,12 @@ import { import { CoreServicesContext } from '../../../components/CoreServices/CoreServices'; import { CoreStart, MountPoint } from '../../../../../../src/core/public'; import { DataSourceSelectableConfig } from '../../../../../../src/plugins/data_source_management/public'; -import { getDataSourceManagementPlugin, getDataSourcePlugin, getNotifications, getSavedObjectsClient } from '../../../services'; +import { + getDataSourceManagementPlugin, + getDataSourcePlugin, + getNotifications, + getSavedObjectsClient, +} from '../../../services'; import { RouteComponentProps } from 'react-router-dom'; interface OverviewProps extends RouteComponentProps { @@ -70,7 +85,7 @@ export function DashboardOverview(props: OverviewProps) { const errorGettingDetectors = adState.errorMessage; const isLoadingDetectors = adState.requesting; - const dataSourceEnabled = getDataSourcePlugin().dataSourceEnabled; + const dataSourceEnabled = getDataSourcePlugin()?.dataSourceEnabled || false; const [currentDetectors, setCurrentDetectors] = useState( Object.values(allDetectorList) @@ -82,7 +97,9 @@ export function DashboardOverview(props: OverviewProps) { const queryParams = getURLQueryParams(props.location); const [MDSOverviewState, setMDSOverviewState] = useState({ queryParams, - selectedDataSourceId: queryParams.dataSourceId ? queryParams.dataSourceId : '', + selectedDataSourceId: queryParams.dataSourceId + ? queryParams.dataSourceId + : '', }); const getDetectorOptions = (detectorsIdMap: { @@ -140,7 +157,7 @@ export function DashboardOverview(props: OverviewProps) { selectedDataSourceId: dataSourceId, }); } - } + }; const opensearchState = useSelector((state: AppState) => state.opensearch); @@ -191,7 +208,13 @@ export function DashboardOverview(props: OverviewProps) { }; const intializeDetectors = async () => { - dispatch(getDetectorList(getAllDetectorsQueryParamsWithDataSourceId(MDSOverviewState.selectedDataSourceId))); + dispatch( + getDetectorList( + getAllDetectorsQueryParamsWithDataSourceId( + MDSOverviewState.selectedDataSourceId + ) + ) + ); dispatch(getIndices('', MDSOverviewState.selectedDataSourceId)); dispatch(getAliases('', MDSOverviewState.selectedDataSourceId)); }; @@ -204,10 +227,8 @@ export function DashboardOverview(props: OverviewProps) { history.replace({ ...location, search: queryString.stringify(updatedParams), - }) - if (dataSourceEnabled ? MDSOverviewState.selectedDataSourceId : true) { - intializeDetectors(); - } + }); + intializeDetectors(); }, [MDSOverviewState]); useEffect(() => { @@ -237,28 +258,31 @@ export function DashboardOverview(props: OverviewProps) { filterSelectedDetectors( selectedDetectorsName, selectedDetectorStates, - selectedIndices, + selectedIndices ); }, [selectedDetectorsName, selectedIndices, selectedDetectorStates]); - const DataSourceMenu = - getDataSourceManagementPlugin().ui.getDataSourceMenu(); - const renderDataSourceComponent = useMemo(() => { - return ( - - handleDataSourceChange(dataSources), - }} - /> - ); - }, [getSavedObjectsClient(), getNotifications(), props.setActionMenu]); + let renderDataSourceComponent = null; + if (dataSourceEnabled) { + const DataSourceMenu = + getDataSourceManagementPlugin().ui.getDataSourceMenu(); + renderDataSourceComponent = useMemo(() => { + return ( + + handleDataSourceChange(dataSources), + }} + /> + ); + }, [getSavedObjectsClient(), getNotifications(), props.setActionMenu]); + } return (
    @@ -312,8 +336,7 @@ export function DashboardOverview(props: OverviewProps) { - + diff --git a/public/pages/DetectorConfig/containers/DetectorConfig.tsx b/public/pages/DetectorConfig/containers/DetectorConfig.tsx index 0539c5a9..f2fec2d1 100644 --- a/public/pages/DetectorConfig/containers/DetectorConfig.tsx +++ b/public/pages/DetectorConfig/containers/DetectorConfig.tsx @@ -29,7 +29,8 @@ interface DetectorConfigProps extends RouteComponentProps { export function DetectorConfig(props: DetectorConfigProps) { const dispatch = useDispatch(); const location = useLocation(); - const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; + const dataSourceId = + new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; const detector = useSelector( (state: AppState) => state.ad.detectors[props.detectorId] ); diff --git a/public/pages/DetectorDetail/containers/DetectorDetail.tsx b/public/pages/DetectorDetail/containers/DetectorDetail.tsx index 6fd7f10d..80ef2fbe 100644 --- a/public/pages/DetectorDetail/containers/DetectorDetail.tsx +++ b/public/pages/DetectorDetail/containers/DetectorDetail.tsx @@ -27,7 +27,13 @@ import { import { CoreStart, MountPoint } from '../../../../../../src/core/public'; import { CoreServicesContext } from '../../../components/CoreServices/CoreServices'; import { get, isEmpty } from 'lodash'; -import { RouteComponentProps, Switch, Route, Redirect, useLocation } from 'react-router-dom'; +import { + RouteComponentProps, + Switch, + Route, + Redirect, + useLocation, +} from 'react-router-dom'; import { useDispatch, useSelector } from 'react-redux'; import { useFetchDetectorInfo } from '../../CreateDetectorSteps/hooks/useFetchDetectorInfo'; import { useHideSideNavBar } from '../../main/hooks/useHideSideNavBar'; @@ -59,7 +65,12 @@ import { DETECTOR_STATE } from '../../../../server/utils/constants'; import { CatIndex } from '../../../../server/models/types'; import { containsIndex } from '../utils/helpers'; import { DataSourceViewConfig } from '../../../../../../src/plugins/data_source_management/public'; -import { getDataSourceManagementPlugin, getDataSourcePlugin, getNotifications, getSavedObjectsClient } from '../../../services'; +import { + getDataSourceManagementPlugin, + getDataSourcePlugin, + getNotifications, + getSavedObjectsClient, +} from '../../../services'; export interface DetectorRouterProps { detectorId?: string; @@ -106,13 +117,18 @@ export const DetectorDetail = (props: DetectorDetailProps) => { const core = React.useContext(CoreServicesContext) as CoreStart; const dispatch = useDispatch(); const detectorId = get(props, 'match.params.detectorId', '') as string; - const dataSourceEnabled = getDataSourcePlugin().dataSourceEnabled; + const dataSourceEnabled = getDataSourcePlugin()?.dataSourceEnabled || false; const location = useLocation(); - const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; - + const dataSourceId = + new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; + const { detector, hasError, isLoadingDetector, errorMessage } = useFetchDetectorInfo(detectorId, dataSourceId); - const { monitor} = useFetchMonitorInfo(detectorId, dataSourceId, dataSourceEnabled); + const { monitor } = useFetchMonitorInfo( + detectorId, + dataSourceId, + dataSourceEnabled + ); const visibleIndices = useSelector( (state: AppState) => state.opensearch.indices ) as CatIndex[]; @@ -161,17 +177,15 @@ export const DetectorDetail = (props: DetectorDetailProps) => { // Getting all visible indices. Will re-fetch if changes to the detector (e.g., // detector starts, result index recreated or user switches tabs to re-fetch detector) useEffect(() => { - if (dataSourceEnabled ? dataSourceId : true) { - const getInitialIndices = async () => { - await dispatch(getIndices('', dataSourceId)).catch((error: any) => { - console.error(error); - core.notifications.toasts.addDanger('Error getting all indices'); - }); - }; - // only need to check if indices exist after detector finishes loading - if (!isLoadingDetector) { - getInitialIndices(); - } + const getInitialIndices = async () => { + await dispatch(getIndices('', dataSourceId)).catch((error: any) => { + console.error(error); + core.notifications.toasts.addDanger('Error getting all indices'); + }); + }; + // only need to check if indices exist after detector finishes loading + if (!isLoadingDetector) { + getInitialIndices(); } }, [detector]); @@ -209,7 +223,9 @@ export const DetectorDetail = (props: DetectorDetailProps) => { ...detectorDetailModel, selectedTab: DETECTOR_DETAIL_TABS.CONFIGURATIONS, }); - props.history.push(`/detectors/${detectorId}/configurations?dataSourceId=${dataSourceId}`); + props.history.push( + `/detectors/${detectorId}/configurations?dataSourceId=${dataSourceId}` + ); }, []); const handleSwitchToHistoricalTab = useCallback(() => { @@ -217,7 +233,9 @@ export const DetectorDetail = (props: DetectorDetailProps) => { ...detectorDetailModel, selectedTab: DETECTOR_DETAIL_TABS.HISTORICAL, }); - props.history.push(`/detectors/${detectorId}/historical?dataSourceId=${dataSourceId}`); + props.history.push( + `/detectors/${detectorId}/historical?dataSourceId=${dataSourceId}` + ); }, []); const handleTabChange = (route: DETECTOR_DETAIL_TABS) => { @@ -225,7 +243,9 @@ export const DetectorDetail = (props: DetectorDetailProps) => { ...detectorDetailModel, selectedTab: route, }); - props.history.push(`/detectors/${detectorId}/${route}?dataSourceId=${dataSourceId}`); + props.history.push( + `/detectors/${detectorId}/${route}?dataSourceId=${dataSourceId}` + ); }; const hideMonitorCalloutModal = () => { @@ -358,7 +378,23 @@ export const DetectorDetail = (props: DetectorDetailProps) => { > ) : null; - const DataSourceMenu = getDataSourceManagementPlugin().ui.getDataSourceMenu(); + let renderDataSourceComponent = null; + if (dataSourceEnabled) { + const DataSourceMenu = + getDataSourceManagementPlugin().ui.getDataSourceMenu(); + renderDataSourceComponent = ( + + ); + } return ( @@ -371,19 +407,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { : { ...lightStyles, flexGrow: 'unset' }), }} > - - {dataSourceEnabled && ( - - )} + {dataSourceEnabled && renderDataSourceComponent} { const dispatch = useDispatch(); useEffect(() => { - if (dataSourceEnabled ? dataSourceId : true) { - dispatch(searchMonitors(dataSourceId)); - } + dispatch(searchMonitors(dataSourceId)); }, []); const isMonitorRequesting = useSelector( diff --git a/public/pages/DetectorResults/containers/AnomalyHistory.tsx b/public/pages/DetectorResults/containers/AnomalyHistory.tsx index a105d98d..8cb86217 100644 --- a/public/pages/DetectorResults/containers/AnomalyHistory.tsx +++ b/public/pages/DetectorResults/containers/AnomalyHistory.tsx @@ -128,7 +128,8 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { ? props.detector.detectionDateRange.endTime : moment().valueOf(); const location = useLocation(); - const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; + const dataSourceId = + new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; const [dateRange, setDateRange] = useState({ startDate: initialStartDate, endDate: initialEndDate, @@ -226,7 +227,9 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { taskId.current, modelId ); - return dispatch(searchResults(params, resultIndex, dataSourceId, true)); + return dispatch( + searchResults(params, resultIndex, dataSourceId, true) + ); }) : []; @@ -278,7 +281,9 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { taskId.current, modelId ); - return dispatch(searchResults(params, resultIndex, dataSourceId, true)); + return dispatch( + searchResults(params, resultIndex, dataSourceId, true) + ); } ); @@ -311,7 +316,12 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { modelId ); const bucketizedAnomalyResultResponse = await dispatch( - searchResults(bucketizedAnomalyResultsQuery, resultIndex, dataSourceId, true) + searchResults( + bucketizedAnomalyResultsQuery, + resultIndex, + dataSourceId, + true + ) ); allBucketizedAnomalyResults.push( parseBucketizedAnomalyResults(bucketizedAnomalyResultResponse) @@ -411,7 +421,14 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { ); const detectorResultResponse = props.isHistorical ? await dispatch( - getDetectorResults(taskId.current, dataSourceId, params, true, resultIndex, true) + getDetectorResults( + taskId.current, + dataSourceId, + params, + true, + resultIndex, + true + ) ).catch((error: any) => { setIsLoading(false); setIsLoadingAnomalyResults(false); @@ -558,7 +575,9 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { props.isHistorical, taskId.current ); - const result = await dispatch(searchResults(query, resultIndex, dataSourceId, true)); + const result = await dispatch( + searchResults(query, resultIndex, dataSourceId, true) + ); topEntityAnomalySummaries = parseTopEntityAnomalySummaryResults( result, isMultiCategory @@ -577,7 +596,9 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { props.isHistorical, taskId.current ); - return dispatch(searchResults(entityResultQuery, resultIndex, dataSourceId, true)); + return dispatch( + searchResults(entityResultQuery, resultIndex, dataSourceId, true) + ); } ); @@ -728,7 +749,9 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => { heatmapCell.entityList ); - const result = await dispatch(searchResults(query, resultIndex, dataSourceId, true)); + const result = await dispatch( + searchResults(query, resultIndex, dataSourceId, true) + ); // Gets top child entities as an Entity[][], // where each entry in the array is a unique combination of entity values diff --git a/public/pages/DetectorResults/containers/AnomalyResults.tsx b/public/pages/DetectorResults/containers/AnomalyResults.tsx index 377bfbb6..d09e26ab 100644 --- a/public/pages/DetectorResults/containers/AnomalyResults.tsx +++ b/public/pages/DetectorResults/containers/AnomalyResults.tsx @@ -68,8 +68,6 @@ import { SampleIndexDetailsCallout } from '../../Overview/components/SampleIndex import { CoreStart } from '../../../../../../src/core/public'; import { CoreServicesContext } from '../../../components/CoreServices/CoreServices'; import { DEFAULT_SHINGLE_SIZE } from '../../../utils/constants'; -import { getDataSourcePlugin } from '../../../../public/services'; - interface AnomalyResultsProps extends RouteComponentProps { detectorId: string; @@ -83,12 +81,12 @@ export function AnomalyResults(props: AnomalyResultsProps) { const core = React.useContext(CoreServicesContext) as CoreStart; const dispatch = useDispatch(); const detectorId = props.detectorId; - const detector = useSelector( + const detector = useSelector( (state: AppState) => state.ad.detectors[detectorId] ); - const dataSourceEnabled = getDataSourcePlugin().dataSourceEnabled; const location = useLocation(); - const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; + const dataSourceId = + new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; useEffect(() => { core.chrome.setBreadcrumbs([ @@ -96,15 +94,11 @@ export function AnomalyResults(props: AnomalyResultsProps) { BREADCRUMBS.DETECTORS, { text: detector ? detector.name : '' }, ]); - if (dataSourceEnabled ? dataSourceId : true) { - dispatch(getDetector(detectorId, dataSourceId)); - } + dispatch(getDetector(detectorId, dataSourceId)); }, []); const fetchDetector = async () => { - if (dataSourceEnabled ? dataSourceId : true) { - dispatch(getDetector(detectorId, dataSourceId)); - } + dispatch(getDetector(detectorId, dataSourceId)); }; useEffect(() => { @@ -257,7 +251,14 @@ export function AnomalyResults(props: AnomalyResultsProps) { try { const resultIndex = get(detector, 'resultIndex', ''); const detectorResultResponse = await dispatch( - getDetectorResults(detectorId, dataSourceId, params, false, resultIndex, true) + getDetectorResults( + detectorId, + dataSourceId, + params, + false, + resultIndex, + true + ) ); const featuresData = get( detectorResultResponse, @@ -560,9 +561,7 @@ export function AnomalyResults(props: AnomalyResultsProps) { ) : null} - + ); -} \ No newline at end of file +} diff --git a/public/pages/DetectorResults/containers/AnomalyResultsLiveChart.tsx b/public/pages/DetectorResults/containers/AnomalyResultsLiveChart.tsx index 5fdca029..d38db529 100644 --- a/public/pages/DetectorResults/containers/AnomalyResultsLiveChart.tsx +++ b/public/pages/DetectorResults/containers/AnomalyResultsLiveChart.tsx @@ -69,7 +69,8 @@ export const AnomalyResultsLiveChart = ( ) => { const dispatch = useDispatch(); const location = useLocation(); - const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; + const dataSourceId = + new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; const [firstLoading, setFirstLoading] = useState(true); const [isFullScreen, setIsFullScreen] = useState(false); @@ -134,7 +135,7 @@ export const AnomalyResultsLiveChart = ( detectionInterval, intervals ); - await dispatch( + await dispatch( getDetectorLiveResults( detectorId, dataSourceId, diff --git a/public/pages/DetectorsList/containers/List/List.tsx b/public/pages/DetectorsList/containers/List/List.tsx index 94ad679d..f1fc9631 100644 --- a/public/pages/DetectorsList/containers/List/List.tsx +++ b/public/pages/DetectorsList/containers/List/List.tsx @@ -84,7 +84,12 @@ import { import { CoreStart, MountPoint } from '../../../../../../../src/core/public'; import { CoreServicesContext } from '../../../../components/CoreServices/CoreServices'; import { DataSourceSelectableConfig } from '../../../../../../../src/plugins/data_source_management/public'; -import { getDataSourceManagementPlugin, getDataSourcePlugin, getNotifications, getSavedObjectsClient } from '../../../../services'; +import { + getDataSourceManagementPlugin, + getDataSourcePlugin, + getNotifications, + getSavedObjectsClient, +} from '../../../../services'; export interface ListRouterParams { from: string; @@ -132,7 +137,7 @@ export const DetectorList = (props: ListProps) => { (state: AppState) => state.ad.requesting ); - const dataSourceEnabled = getDataSourcePlugin().dataSourceEnabled; + const dataSourceEnabled = getDataSourcePlugin()?.dataSourceEnabled || false; const [selectedDetectors, setSelectedDetectors] = useState( [] as DetectorListItem[] @@ -166,9 +171,7 @@ export const DetectorList = (props: ListProps) => { // Getting all initial monitors useEffect(() => { const getInitialMonitors = async () => { - if (dataSourceEnabled ? state.selectedDataSourceId : true) { - dispatch(searchMonitors(state.selectedDataSourceId)); - } + dispatch(searchMonitors(state.selectedDataSourceId)); }; getInitialMonitors(); }, []); @@ -241,9 +244,7 @@ export const DetectorList = (props: ListProps) => { setIsLoadingFinalDetectors(true); - if (dataSourceEnabled ? state.selectedDataSourceId : true) { - getUpdatedDetectors(); - } + getUpdatedDetectors(); }, [ state.page, state.queryParams, @@ -260,14 +261,14 @@ export const DetectorList = (props: ListProps) => { state.selectedIndices, state.selectedDetectorStates, state.queryParams.sortField, - state.queryParams.sortDirection, + state.queryParams.sortDirection ); setSelectedDetectors(curSelectedDetectors); const curDetectorsToDisplay = getDetectorsToDisplay( curSelectedDetectors, state.page, - state.queryParams.size, + state.queryParams.size ); setDetectorsToDisplay(curDetectorsToDisplay); @@ -340,7 +341,9 @@ export const DetectorList = (props: ListProps) => { if (searchValue !== indexQuery) { const sanitizedQuery = sanitizeSearchText(searchValue); setIndexQuery(sanitizedQuery); - await dispatch(getPrioritizedIndices(sanitizedQuery, state.selectedDataSourceId)); + await dispatch( + getPrioritizedIndices(sanitizedQuery, state.selectedDataSourceId) + ); setState((state) => ({ ...state, page: 0, @@ -496,9 +499,7 @@ export const DetectorList = (props: ListProps) => { ); }) .finally(() => { - if (dataSourceEnabled ? state.selectedDataSourceId : true) { - getUpdatedDetectors(); - } + getUpdatedDetectors(); }); }; @@ -668,24 +669,27 @@ export const DetectorList = (props: ListProps) => { const confirmModal = getConfirmModal(); - const DataSourceMenu = - getDataSourceManagementPlugin().ui.getDataSourceMenu(); - const renderDataSourceComponent = useMemo(() => { - return ( - - handleDataSourceChange(dataSources), - }} - /> - ); - }, [getSavedObjectsClient(), getNotifications(), props.setActionMenu]); + let renderDataSourceComponent = null; + if (dataSourceEnabled) { + const DataSourceMenu = + getDataSourceManagementPlugin().ui.getDataSourceMenu(); + renderDataSourceComponent = useMemo(() => { + return ( + + handleDataSourceChange(dataSources), + }} + /> + ); + }, [getSavedObjectsClient(), getNotifications(), props.setActionMenu]); + } const columns = getColumns(state.selectedDataSourceId); diff --git a/public/pages/DetectorsList/utils/helpers.ts b/public/pages/DetectorsList/utils/helpers.ts index e805e460..01dca578 100644 --- a/public/pages/DetectorsList/utils/helpers.ts +++ b/public/pages/DetectorsList/utils/helpers.ts @@ -21,8 +21,15 @@ import { DETECTOR_ACTION } from '../utils/constants'; export const getURLQueryParams = (location: { search: string; }): GetDetectorsQueryParams => { - const { from, size, search, indices, sortField, sortDirection, dataSourceId } = - queryString.parse(location.search) as { [key: string]: string }; + const { + from, + size, + search, + indices, + sortField, + sortDirection, + dataSourceId, + } = queryString.parse(location.search) as { [key: string]: string }; return { // @ts-ignore from: isNaN(parseInt(from, 10)) diff --git a/public/pages/DetectorsList/utils/tableUtils.tsx b/public/pages/DetectorsList/utils/tableUtils.tsx index ba3e5f1d..5f354214 100644 --- a/public/pages/DetectorsList/utils/tableUtils.tsx +++ b/public/pages/DetectorsList/utils/tableUtils.tsx @@ -57,7 +57,7 @@ export function getColumns(dataSourceId) { field: 'name', name: ( - Detector{''} + Detector{''} ), sortable: true, @@ -119,15 +119,9 @@ export function getColumns(dataSourceId) { if (dataSourceId) { href += `?dataSourceId=${dataSourceId}`; } - return ( - - View results - - ); + return View results; } else { - return ( - - - ); + return -; } }, }, @@ -172,5 +166,5 @@ export function getColumns(dataSourceId) { width: '16%', render: renderTime, }, - ]as EuiBasicTableColumn[]; + ] as EuiBasicTableColumn[]; } diff --git a/public/pages/HistoricalDetectorResults/containers/HistoricalDetectorResults.tsx b/public/pages/HistoricalDetectorResults/containers/HistoricalDetectorResults.tsx index adbadc0c..f42509d1 100644 --- a/public/pages/HistoricalDetectorResults/containers/HistoricalDetectorResults.tsx +++ b/public/pages/HistoricalDetectorResults/containers/HistoricalDetectorResults.tsx @@ -64,7 +64,8 @@ export function HistoricalDetectorResults( const dispatch = useDispatch(); const detectorId: string = get(props, 'match.params.detectorId', ''); const location = useLocation(); - const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; + const dataSourceId = + new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; const adState = useSelector((state: AppState) => state.ad); const allDetectors = adState.detectors; @@ -116,7 +117,14 @@ export function HistoricalDetectorResults( const startHistoricalTask = async (startTime: number, endTime: number) => { try { - dispatch(startHistoricalDetector(props.detectorId, dataSourceId, startTime, endTime)) + dispatch( + startHistoricalDetector( + props.detectorId, + dataSourceId, + startTime, + endTime + ) + ) .then((response: any) => { setTaskId(get(response, 'response._id')); core.notifications.toasts.addSuccess( diff --git a/public/pages/Overview/components/SampleDataBox/SampleDataBox.tsx b/public/pages/Overview/components/SampleDataBox/SampleDataBox.tsx index 78f60b66..8a61d126 100644 --- a/public/pages/Overview/components/SampleDataBox/SampleDataBox.tsx +++ b/public/pages/Overview/components/SampleDataBox/SampleDataBox.tsx @@ -39,7 +39,8 @@ interface SampleDataBoxProps { export const SampleDataBox = (props: SampleDataBoxProps) => { const location = useLocation(); - const dataSourceId = new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; + const dataSourceId = + new URLSearchParams(location.search).get(DATA_SOURCE_ID) || ''; return (
    state.ad.requesting); - const isLoadingSampleIndices = useSelector((state: AppState) => state.opensearch.requesting); + const isLoadingSampleDetectors = useSelector( + (state: AppState) => state.ad.requesting + ); + const isLoadingSampleIndices = useSelector( + (state: AppState) => state.opensearch.requesting + ); const dispatch = useDispatch(); const visibleSampleIndices = useSelector( (state: AppState) => state.opensearch.indices @@ -85,7 +92,7 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { const allSampleDetectors = Object.values( useSelector((state: AppState) => state.ad.detectorList) ); - const dataSourceEnabled = getDataSourcePlugin().dataSourceEnabled; + const dataSourceEnabled = getDataSourcePlugin()?.dataSourceEnabled || false; const [isLoadingHttpData, setIsLoadingHttpData] = useState(false); const [isLoadingEcommerceData, setIsLoadingEcommerceData] = useState(false); @@ -97,11 +104,13 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { useState(false); const [showHostHealthDetailsFlyout, setShowHostHealthDetailsFlyout] = useState(false); - + const queryParams = getURLQueryParams(props.location); const [MDSOverviewState, setMDSOverviewState] = useState({ queryParams, - selectedDataSourceId: queryParams.dataSourceId ? queryParams.dataSourceId : '', + selectedDataSourceId: queryParams.dataSourceId + ? queryParams.dataSourceId + : '', }); // Set breadcrumbs on page initialization @@ -118,7 +127,7 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { history.replace({ ...location, search: queryString.stringify(updatedParams), - }) + }); if (dataSourceEnabled ? MDSOverviewState.selectedDataSourceId : true) { fetchData(); @@ -127,12 +136,21 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { // fetch smaple detectors and sample indices const fetchData = async () => { - await dispatch(getDetectorList(getSampleDetectorsQueryParamsWithDataSouceId(MDSOverviewState.selectedDataSourceId))).catch( - (error: any) => { - console.error('Error getting sample detectors: ', error); - } - ); - await dispatch(getIndices(GET_SAMPLE_INDICES_QUERY, MDSOverviewState.selectedDataSourceId)).catch((error: any) => { + await dispatch( + getDetectorList( + getSampleDetectorsQueryParamsWithDataSouceId( + MDSOverviewState.selectedDataSourceId + ) + ) + ).catch((error: any) => { + console.error('Error getting sample detectors: ', error); + }); + await dispatch( + getIndices( + GET_SAMPLE_INDICES_QUERY, + MDSOverviewState.selectedDataSourceId + ) + ).catch((error: any) => { console.error('Error getting sample indices: ', error); }); }; @@ -150,7 +168,9 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { // Create the index (if it doesn't exist yet) if (!containsSampleIndex(visibleSampleIndices, sampleType)) { - await dispatch(createIndex(indexConfig, MDSOverviewState.selectedDataSourceId)).catch((error: any) => { + await dispatch( + createIndex(indexConfig, MDSOverviewState.selectedDataSourceId) + ).catch((error: any) => { errorDuringAction = true; errorMessage = 'Error creating sample index. ' + prettifyErrorMessage(error); @@ -160,7 +180,9 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { // Get the sample data from the server and bulk insert if (!errorDuringAction) { - await dispatch(createSampleData(sampleType, MDSOverviewState.selectedDataSourceId)).catch((error: any) => { + await dispatch( + createSampleData(sampleType, MDSOverviewState.selectedDataSourceId) + ).catch((error: any) => { errorDuringAction = true; errorMessage = prettifyErrorMessage(error.message); console.error('Error bulk inserting data: ', errorMessage); @@ -169,11 +191,15 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { // Create the detector if (!errorDuringAction) { - await dispatch(createDetector(detectorConfig, MDSOverviewState.selectedDataSourceId)) + await dispatch( + createDetector(detectorConfig, MDSOverviewState.selectedDataSourceId) + ) .then(function (response: any) { const detectorId = response.response.id; // Start the detector - dispatch(startDetector(detectorId, MDSOverviewState.selectedDataSourceId)).catch((error: any) => { + dispatch( + startDetector(detectorId, MDSOverviewState.selectedDataSourceId) + ).catch((error: any) => { errorDuringAction = true; errorMessage = prettifyErrorMessage(error.message); console.error('Error starting sample detector: ', errorMessage); @@ -211,26 +237,29 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { selectedDataSourceId: dataSourceId, }); } - } + }; - const DataSourceMenu = - getDataSourceManagementPlugin().ui.getDataSourceMenu(); - const renderDataSourceComponent = useMemo(() => { - return ( - - handleDataSourceChange(dataSources), - }} - /> - ); - }, [getSavedObjectsClient, getNotifications, props.setActionMenu]); + let renderDataSourceComponent = null; + if (dataSourceEnabled) { + const DataSourceMenu = + getDataSourceManagementPlugin().ui.getDataSourceMenu(); + renderDataSourceComponent = useMemo(() => { + return ( + + handleDataSourceChange(dataSources), + }} + /> + ); + }, [getSavedObjectsClient, getNotifications, props.setActionMenu]); + } return isLoadingSampleDetectors && isLoadingSampleIndices ? (
    diff --git a/public/pages/main/Main.tsx b/public/pages/main/Main.tsx index 1167acc8..d3848abf 100644 --- a/public/pages/main/Main.tsx +++ b/public/pages/main/Main.tsx @@ -48,16 +48,34 @@ export function Main(props: MainProps) { const totalDetectors = adState.totalDetectors; const queryParams = getURLQueryParams(props.location); const dataSourceId = queryParams.dataSourceId ? queryParams.dataSourceId : ''; - const existingParams = "from=0&size=20&search=&indices=&sortField=name&sortDirection=asc"; - const constructHrefWithDataSourceId = (basePath, existingParams, dataSourceId) => { - const fullUrl = `${window.location.origin}${basePath}?${existingParams}`; - const url = new URL(fullUrl); - url.searchParams.set('dataSourceId', dataSourceId); - - return `#${url.pathname}${url.search}`; + const existingParams = + 'from=0&size=20&search=&indices=&sortField=name&sortDirection=asc'; + + const constructHrefWithDataSourceId = ( + basePath, + existingParams = '', + dataSourceId = '' + ) => { + const searchParams = new URLSearchParams(existingParams); + + if (dataSourceId) { + searchParams.set('dataSourceId', dataSourceId); + } + + return `#${basePath}?${searchParams.toString()}`; }; - const dashboardHref = dataSourceId ? `#${APP_PATH.DASHBOARD}?dataSourceId=${dataSourceId}` : `#${APP_PATH.DASHBOARD}`; - const overviewHref = dataSourceId ? `#${APP_PATH.OVERVIEW}?dataSourceId=${dataSourceId}` : `#${APP_PATH.OVERVIEW}`; + + // Usage examples: + const dashboardHref = constructHrefWithDataSourceId( + APP_PATH.DASHBOARD, + '', + dataSourceId + ); + const overviewHref = constructHrefWithDataSourceId( + APP_PATH.OVERVIEW, + '', + dataSourceId + ); const sideNav = [ { @@ -74,7 +92,11 @@ export function Main(props: MainProps) { { name: Navigation.Detectors, id: 2, - href: constructHrefWithDataSourceId(APP_PATH.LIST_DETECTORS, existingParams, dataSourceId), + href: constructHrefWithDataSourceId( + APP_PATH.LIST_DETECTORS, + existingParams, + dataSourceId + ), isSelected: props.location.pathname === APP_PATH.LIST_DETECTORS, }, ], @@ -93,11 +115,12 @@ export function Main(props: MainProps) { - ( + } + /> + )} /> ( - + {...props} + /> )} /> )} /> - + render={(props: RouteComponentProps) => totalDetectors > 0 ? ( - diff --git a/public/pages/utils/helpers.ts b/public/pages/utils/helpers.ts index c0126021..23f8d5d8 100644 --- a/public/pages/utils/helpers.ts +++ b/public/pages/utils/helpers.ts @@ -68,7 +68,7 @@ export const filterAndSortDetectors = ( selectedIndices: string[], selectedDetectorStates: DETECTOR_STATE[], sortField: string, - sortDirection: string, + sortDirection: string ) => { let filteredBySearch = search == '' @@ -96,7 +96,7 @@ export const filterAndSortDetectors = ( export const getDetectorsToDisplay = ( detectors: DetectorListItem[], page: number, - size: number, + size: number ) => { return detectors.slice(size * page, page * size + size); }; @@ -125,8 +125,9 @@ export const getAllDetectorsQueryParamsWithDataSourceId = ( dataSourceId: dataSourceId, }); -export const getSampleDetectorsQueryParamsWithDataSouceId = (dataSourceId: string) => ( - { +export const getSampleDetectorsQueryParamsWithDataSouceId = ( + dataSourceId: string +) => ({ from: 0, search: 'sample', indices: '', diff --git a/public/plugin.ts b/public/plugin.ts index d44a9f91..07eb6194 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -88,10 +88,7 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin mount: async (params: AppMountParameters) => { const { renderApp } = await import('./anomaly_detection_app'); const [coreStart] = await core.getStartServices(); - return renderApp( - coreStart, - params, - ); + return renderApp(coreStart, params); }, }); diff --git a/public/redux/reducers/ad.ts b/public/redux/reducers/ad.ts index 1191cf8f..ed0aea0f 100644 --- a/public/redux/reducers/ad.ts +++ b/public/redux/reducers/ad.ts @@ -369,16 +369,21 @@ const reducer = handleActions( initialDetectorsState ); -export const createDetector = (requestBody: Detector, dataSourceId = ''): APIAction => { - const url = dataSourceId ? `..${AD_NODE_API.DETECTOR}/${dataSourceId}` : `..${AD_NODE_API.DETECTOR}`; +export const createDetector = ( + requestBody: Detector, + dataSourceId = '' +): APIAction => { + const url = dataSourceId + ? `..${AD_NODE_API.DETECTOR}/${dataSourceId}` + : `..${AD_NODE_API.DETECTOR}`; return { type: CREATE_DETECTOR, request: (client: HttpSetup) => - client.post(url, { - body: JSON.stringify(requestBody), - }), - } + client.post(url, { + body: JSON.stringify(requestBody), + }), + }; }; export const validateDetector = ( @@ -392,19 +397,29 @@ export const validateDetector = ( }), }); -export const getDetector = (detectorId: string, dataSourceId = ''): APIAction => { +export const getDetector = ( + detectorId: string, + dataSourceId = '' +): APIAction => { const baseUrl = `..${AD_NODE_API.DETECTOR}`; - const url = dataSourceId ? `${baseUrl}/${detectorId}/${dataSourceId}` : `${baseUrl}/${detectorId}`; + const url = dataSourceId + ? `${baseUrl}/${detectorId}/${dataSourceId}` + : `${baseUrl}/${detectorId}`; return { type: GET_DETECTOR, - request: (client: HttpSetup) => client.get(url), detectorId - } + request: (client: HttpSetup) => client.get(url), + detectorId, + }; }; -export const getDetectorList = (queryParams: GetDetectorsQueryParams): APIAction => { +export const getDetectorList = ( + queryParams: GetDetectorsQueryParams +): APIAction => { const baseUrl = `..${AD_NODE_API.DETECTOR}/_list`; - const url = queryParams.dataSourceId ? `${baseUrl}/${queryParams.dataSourceId}` : baseUrl; + const url = queryParams.dataSourceId + ? `${baseUrl}/${queryParams.dataSourceId}` + : baseUrl; return { type: GET_DETECTOR_LIST, @@ -432,25 +447,33 @@ export const updateDetector = ( detectorId, }); -export const deleteDetector = (detectorId: string, dataSourceId = ''): APIAction => { +export const deleteDetector = ( + detectorId: string, + dataSourceId = '' +): APIAction => { const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; return { type: DELETE_DETECTOR, - request: (client: HttpSetup) => client.delete(url), detectorId, + request: (client: HttpSetup) => client.delete(url), + detectorId, }; }; -export const startDetector = (detectorId: string, dataSourceId = ''): APIAction => { - const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; - const url = dataSourceId ? `${baseUrl}/${dataSourceId}/start` : `${baseUrl}/start`; +export const startDetector = ( + detectorId: string, + dataSourceId = '' +): APIAction => { + const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}/start`; + const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; return { type: START_DETECTOR, - request: (client: HttpSetup) => client.post(url), detectorId, - } -} + request: (client: HttpSetup) => client.post(url), + detectorId, + }; +}; export const startHistoricalDetector = ( detectorId: string, @@ -459,7 +482,9 @@ export const startHistoricalDetector = ( endTime: number ): APIAction => { const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; - const url = dataSourceId ? `${baseUrl}/${dataSourceId}/start` : `${baseUrl}/start`; + const url = dataSourceId + ? `${baseUrl}/${dataSourceId}/start` + : `${baseUrl}/start`; return { type: START_HISTORICAL_DETECTOR, @@ -476,23 +501,33 @@ export const startHistoricalDetector = ( }; }; -export const stopDetector = (detectorId: string, dataSourceId = ''): APIAction => { - const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; - const url = dataSourceId ? `${baseUrl}/${dataSourceId}/stop/${false}` : `${baseUrl}/stop/${false}`; +export const stopDetector = ( + detectorId: string, + dataSourceId = '' +): APIAction => { + const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}/stop/${false}`; + const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; return { type: STOP_DETECTOR, - request: (client: HttpSetup) => client.post(url), detectorId, + request: (client: HttpSetup) => client.post(url), + detectorId, }; }; -export const stopHistoricalDetector = (detectorId: string, dataSourceId = ''): APIAction => { +export const stopHistoricalDetector = ( + detectorId: string, + dataSourceId = '' +): APIAction => { const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; - const url = dataSourceId ? `${baseUrl}/${dataSourceId}/stop/${true}` : `${baseUrl}/stop/${true}`; + const url = dataSourceId + ? `${baseUrl}/${dataSourceId}/stop/${true}` + : `${baseUrl}/stop/${true}`; return { type: STOP_HISTORICAL_DETECTOR, - request: (client: HttpSetup) => client.post(url), detectorId, + request: (client: HttpSetup) => client.post(url), + detectorId, }; }; diff --git a/public/redux/reducers/alerting.ts b/public/redux/reducers/alerting.ts index 99aef074..ece38e43 100644 --- a/public/redux/reducers/alerting.ts +++ b/public/redux/reducers/alerting.ts @@ -94,10 +94,10 @@ const reducer = handleActions( initialDetectorsState ); -export const searchMonitors = ( dataSourceId = ''): APIAction => { +export const searchMonitors = (dataSourceId = ''): APIAction => { const baseUrl = `..${ALERTING_NODE_API._SEARCH}`; const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; - + return { type: SEARCH_MONITORS, request: (client: HttpSetup) => client.post(url), diff --git a/public/redux/reducers/anomalyResults.ts b/public/redux/reducers/anomalyResults.ts index 58a9c32c..845e3bb4 100644 --- a/public/redux/reducers/anomalyResults.ts +++ b/public/redux/reducers/anomalyResults.ts @@ -100,18 +100,16 @@ export const getDetectorResults = ( resultIndex: string, onlyQueryCustomResultIndex: boolean ): APIAction => { - let baseUrl = `..${AD_NODE_API.DETECTOR}/${id}`; + let url = `..${AD_NODE_API.DETECTOR}/${id}/results/${isHistorical}`; - // append dataSourceId to the url if it has a truthy value - if (dataSourceId) { - baseUrl += `/${dataSourceId}`; - } - // construct the final url based on whether resultIndex is provided - let url = baseUrl + `/results/${isHistorical}`; if (resultIndex) { url += `/${resultIndex}/${onlyQueryCustomResultIndex}`; } + if (dataSourceId) { + url += `/${dataSourceId}`; + } + return { type: DETECTOR_RESULTS, request: (client: HttpSetup) => client.get(url, { query: queryParams }), @@ -124,18 +122,20 @@ export const searchResults = ( dataSourceId = '', onlyQueryCustomResultIndex: boolean ): APIAction => { - let baseUrl = `..${AD_NODE_API.DETECTOR}/results`; + let baseUrl = `..${AD_NODE_API.DETECTOR}/results/_search`; + + if (resultIndex) { + baseUrl += `/${resultIndex}/${onlyQueryCustomResultIndex}`; + } + if (dataSourceId) { baseUrl += `/${dataSourceId}`; } - let url = baseUrl + '/_search'; - if (resultIndex) { - url += `/${resultIndex}/${onlyQueryCustomResultIndex}`; - } return { type: SEARCH_ANOMALY_RESULTS, - request: (client: HttpSetup) => client.post(url, { body: JSON.stringify(requestBody) }), + request: (client: HttpSetup) => + client.post(baseUrl, { body: JSON.stringify(requestBody) }), }; }; @@ -145,15 +145,13 @@ export const getTopAnomalyResults = ( isHistorical: boolean, requestBody: any ): APIAction => { - let baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; - if (dataSourceId) { - baseUrl += `/${dataSourceId}`; - } - const url = `${baseUrl}/_topAnomalies/${isHistorical}`; + const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}/_topAnomalies/${isHistorical}`; + const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; return { type: GET_TOP_ANOMALY_RESULTS, - request: (client: HttpSetup) => client.post(url, { body: JSON.stringify(requestBody) }), + request: (client: HttpSetup) => + client.post(url, { body: JSON.stringify(requestBody) }), }; }; diff --git a/public/redux/reducers/liveAnomalyResults.ts b/public/redux/reducers/liveAnomalyResults.ts index e82291a9..aa06efef 100644 --- a/public/redux/reducers/liveAnomalyResults.ts +++ b/public/redux/reducers/liveAnomalyResults.ts @@ -57,19 +57,21 @@ const reducer = handleActions( export const getDetectorLiveResults = ( detectorId: string, - dataSourceId: string = '', + dataSourceId: string = '', queryParams: DetectorResultsQueryParams, isHistorical: boolean, resultIndex: string, onlyQueryCustomResultIndex: boolean ): APIAction => { - let baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; + let url = `..${AD_NODE_API.DETECTOR}/${detectorId}/results/${isHistorical}`; + + if (resultIndex) { + url += `/${resultIndex}/${onlyQueryCustomResultIndex}`; + } + if (dataSourceId) { - baseUrl += `/${dataSourceId}`; + url += `/${dataSourceId}`; } - const url = !resultIndex - ? `${baseUrl}/results/${isHistorical}` - : `${baseUrl}/results/${isHistorical}/${resultIndex}/${onlyQueryCustomResultIndex}`; return { type: DETECTOR_LIVE_RESULTS, diff --git a/public/redux/reducers/opensearch.ts b/public/redux/reducers/opensearch.ts index 3b72f802..afd095ca 100644 --- a/public/redux/reducers/opensearch.ts +++ b/public/redux/reducers/opensearch.ts @@ -252,17 +252,22 @@ export const getIndices = (searchKey = '', dataSourceId = '') => { return { type: GET_INDICES, - request: (client: HttpSetup) => client.get(url, { query: { index: searchKey } }), + request: (client: HttpSetup) => + client.get(url, { query: { index: searchKey } }), }; }; -export const getAliases = (searchKey: string = '', dataSourceId = ''): APIAction => { +export const getAliases = ( + searchKey: string = '', + dataSourceId = '' +): APIAction => { const baseUrl = `..${AD_NODE_API._ALIASES}`; const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; return { type: GET_ALIASES, - request: (client: HttpSetup) => client.get(url, { query: { alias: searchKey } }), + request: (client: HttpSetup) => + client.get(url, { query: { alias: searchKey } }), }; }; @@ -283,7 +288,9 @@ export const searchOpenSearch = (requestData: any): APIAction => ({ }); export const createIndex = (indexConfig: any, dataSourceId = ''): APIAction => { - const url = dataSourceId ? `${AD_NODE_API.CREATE_INDEX}/${dataSourceId}` : AD_NODE_API.CREATE_INDEX; + const url = dataSourceId + ? `${AD_NODE_API.CREATE_INDEX}/${dataSourceId}` + : AD_NODE_API.CREATE_INDEX; return { type: CREATE_INDEX, request: (client: HttpSetup) => @@ -291,10 +298,12 @@ export const createIndex = (indexConfig: any, dataSourceId = ''): APIAction => { body: JSON.stringify(indexConfig), }), }; -} +}; export const bulk = (body: any, dataSourceId = ''): APIAction => { - const url = dataSourceId ? `${AD_NODE_API.BULK}/${dataSourceId}` : AD_NODE_API.BULK; + const url = dataSourceId + ? `${AD_NODE_API.BULK}/${dataSourceId}` + : AD_NODE_API.BULK; return { type: BULK, request: (client: HttpSetup) => diff --git a/public/redux/reducers/sampleData.ts b/public/redux/reducers/sampleData.ts index 8511c19c..a3e19ddd 100644 --- a/public/redux/reducers/sampleData.ts +++ b/public/redux/reducers/sampleData.ts @@ -54,15 +54,17 @@ const reducer = handleActions( initialState ); -export const createSampleData = (sampleDataType: SAMPLE_TYPE, dataSourceId = ''): APIAction => { - const url = dataSourceId ? - `..${AD_NODE_API.CREATE_SAMPLE_DATA}/${sampleDataType}/${dataSourceId}` : - `..${AD_NODE_API.CREATE_SAMPLE_DATA}/${sampleDataType}`; +export const createSampleData = ( + sampleDataType: SAMPLE_TYPE, + dataSourceId = '' +): APIAction => { + const url = dataSourceId + ? `..${AD_NODE_API.CREATE_SAMPLE_DATA}/${sampleDataType}/${dataSourceId}` + : `..${AD_NODE_API.CREATE_SAMPLE_DATA}/${sampleDataType}`; return { type: CREATE_SAMPLE_DATA, - request: (client: HttpSetup) => - client.post(url), + request: (client: HttpSetup) => client.post(url), }; }; diff --git a/public/services.ts b/public/services.ts index 705d4d3e..4cb502f1 100644 --- a/public/services.ts +++ b/public/services.ts @@ -44,10 +44,10 @@ export const [getQueryService, setQueryService] = export const [getSavedObjectsClient, setSavedObjectsClient] = createGetterSetter('SavedObjectsClient'); -export const [getDataSourceManagementPlugin, setDataSourceManagementPlugin] = +export const [getDataSourceManagementPlugin, setDataSourceManagementPlugin] = createGetterSetter('DataSourceManagement'); -export const [getDataSourcePlugin, setDataSourcePlugin] = +export const [getDataSourcePlugin, setDataSourcePlugin] = createGetterSetter('DataSource'); // This is primarily used for mocking this module and each of its fns in tests. diff --git a/server/models/types.ts b/server/models/types.ts index f4bbaa59..ab494971 100644 --- a/server/models/types.ts +++ b/server/models/types.ts @@ -111,7 +111,7 @@ export type DetectorResultsQueryParams = { export type MDSQueryParams = { dataSourceId?: string; -} +}; export type Entity = { name: string; diff --git a/server/routes/ad.ts b/server/routes/ad.ts index b04343cd..033365d2 100644 --- a/server/routes/ad.ts +++ b/server/routes/ad.ts @@ -65,59 +65,76 @@ export function registerADRoutes(apiRouter: Router, adService: AdService) { apiRouter.post('/detectors/{dataSourceId}', adService.putDetector); // put detector - apiRouter.put('/detectors/{detectorId}', - adService.putDetector); - apiRouter.put('/detectors/{detectorId}/{dataSourceId}', - adService.putDetector); + apiRouter.put('/detectors/{detectorId}', adService.putDetector); + apiRouter.put( + '/detectors/{detectorId}/{dataSourceId}', + adService.putDetector + ); apiRouter.post('/detectors/_search', adService.searchDetector); - // post search anomaly results - apiRouter.post('/detectors/results/_search/', - adService.searchResults); - apiRouter.post('/detectors/results/{dataSourceId}/_search', - adService.searchResults); - apiRouter.post('/detectors/results/_search/{resultIndex}/{onlyQueryCustomResultIndex}', - adService.searchResults); - apiRouter.post('/detectors/results/{dataSourceId}/_search/{resultIndex}/{onlyQueryCustomResultIndex}', - adService.searchResults); + apiRouter.post('/detectors/results/_search/', adService.searchResults); + apiRouter.post( + '/detectors/results/_search/{dataSourceId}', + adService.searchResults + ); + apiRouter.post( + '/detectors/results/_search/{resultIndex}/{onlyQueryCustomResultIndex}', + adService.searchResults + ); + apiRouter.post( + '/detectors/results/_search/{resultIndex}/{onlyQueryCustomResultIndex}/{dataSourceId}', + adService.searchResults + ); // list detectors - apiRouter.get('/detectors/_list', - adService.getDetectors); - apiRouter.get('/detectors/_list/{dataSourceId}', - adService.getDetectors); + apiRouter.get('/detectors/_list', adService.getDetectors); + apiRouter.get('/detectors/_list/{dataSourceId}', adService.getDetectors); apiRouter.post('/detectors/preview', adService.previewDetector); // get detector anomaly results - apiRouter.get('/detectors/{id}/results/{isHistorical}/{resultIndex}/{onlyQueryCustomResultIndex}', - adService.getAnomalyResults); - apiRouter.get('/detectors/{id}/{dataSourceId}/results/{isHistorical}/{resultIndex}/{onlyQueryCustomResultIndex}', - adService.getAnomalyResults); - apiRouter.get('/detectors/{id}/results/{isHistorical}', - adService.getAnomalyResults); - apiRouter.get('/detectors/{id}/{dataSourceId}/results/{isHistorical}', - adService.getAnomalyResults); + apiRouter.get( + '/detectors/{id}/results/{isHistorical}/{resultIndex}/{onlyQueryCustomResultIndex}', + adService.getAnomalyResults + ); + apiRouter.get( + '/detectors/{id}/results/{isHistorical}/{resultIndex}/{onlyQueryCustomResultIndex}/{dataSourceId}', + adService.getAnomalyResults + ); + apiRouter.get( + '/detectors/{id}/results/{isHistorical}', + adService.getAnomalyResults + ); + apiRouter.get( + '/detectors/{id}/results/{isHistorical}/{dataSourceId}', + adService.getAnomalyResults + ); // delete detector - apiRouter.delete('/detectors/{detectorId}', - adService.deleteDetector); - apiRouter.delete('/detectors/{detectorId}/{dataSourceId}', - adService.deleteDetector); + apiRouter.delete('/detectors/{detectorId}', adService.deleteDetector); + apiRouter.delete( + '/detectors/{detectorId}/{dataSourceId}', + adService.deleteDetector + ); // start detector - apiRouter.post('/detectors/{detectorId}/start', - adService.startDetector); - apiRouter.post('/detectors/{detectorId}/{dataSourceId}/start', - adService.startDetector); + apiRouter.post('/detectors/{detectorId}/start', adService.startDetector); + apiRouter.post( + '/detectors/{detectorId}/start/{dataSourceId}', + adService.startDetector + ); // stop detector - apiRouter.post('/detectors/{detectorId}/stop/{isHistorical}', - adService.stopDetector); - apiRouter.post('/detectors/{detectorId}/{dataSourceId}/stop/{isHistorical}', - adService.stopDetector); + apiRouter.post( + '/detectors/{detectorId}/stop/{isHistorical}', + adService.stopDetector + ); + apiRouter.post( + '/detectors/{detectorId}/stop/{isHistorical}/{dataSourceId}', + adService.stopDetector + ); apiRouter.get( '/detectors/{detectorId}/_profile', @@ -125,19 +142,24 @@ export function registerADRoutes(apiRouter: Router, adService: AdService) { ); // get detector - apiRouter.get('/detectors/{detectorId}', - adService.getDetector); - apiRouter.get('/detectors/{detectorId}/{dataSourceId}', - adService.getDetector); + apiRouter.get('/detectors/{detectorId}', adService.getDetector); + apiRouter.get( + '/detectors/{detectorId}/{dataSourceId}', + adService.getDetector + ); apiRouter.get('/detectors/{detectorName}/_match', adService.matchDetector); apiRouter.get('/detectors/_count', adService.getDetectorCount); // post get top anomaly results - apiRouter.post('/detectors/{detectorId}/_topAnomalies/{isHistorical}', - adService.getTopAnomalyResults); - apiRouter.post('/detectors/{detectorId}/{dataSourceId}/_topAnomalies/{isHistorical}', - adService.getTopAnomalyResults); + apiRouter.post( + '/detectors/{detectorId}/_topAnomalies/{isHistorical}', + adService.getTopAnomalyResults + ); + apiRouter.post( + '/detectors/{detectorId}/_topAnomalies/{isHistorical}/{dataSourceId}', + adService.getTopAnomalyResults + ); apiRouter.post( '/detectors/_validate/{validationType}', @@ -161,18 +183,18 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; - const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + const { dataSourceId = '' } = request.params as { dataSourceId?: string }; const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client); + this.client + ); - const response = await callWithRequest( - 'ad.deleteDetector', { - detectorId, - }); + const response = await callWithRequest('ad.deleteDetector', { + detectorId, + }); return opensearchDashboardsResponse.ok({ body: { @@ -231,7 +253,7 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; - const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + const { dataSourceId = '' } = request.params as { dataSourceId?: string }; //@ts-ignore const ifSeqNo = request.body.seqNo; @@ -250,19 +272,19 @@ export default class AdService { let response; const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client); - + this.client + ); if (isNumber(ifSeqNo) && isNumber(ifPrimaryTerm)) { response = await callWithRequest('ad.updateDetector', params); } else { response = await callWithRequest('ad.createDetector', { - body: params.body, - }); + body: params.body, + }); } const resp = { ...response.anomaly_detector, @@ -329,18 +351,18 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; - const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + const { dataSourceId = '' } = request.params as { dataSourceId?: string }; const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client); + this.client + ); - const detectorResponse = await callWithRequest( - 'ad.getDetector', { - detectorId, - }); + const detectorResponse = await callWithRequest('ad.getDetector', { + detectorId, + }); // Populating static detector fields const staticFields = { @@ -358,21 +380,20 @@ export default class AdService { let historicalTasksResponse = {} as any; try { const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client); - - realtimeTasksResponse = await callWithRequest( - 'ad.searchTasks', { - body: getLatestTaskForDetectorQuery(detectorId, true), - }); + this.client + ); + + realtimeTasksResponse = await callWithRequest('ad.searchTasks', { + body: getLatestTaskForDetectorQuery(detectorId, true), + }); historicalTasksResponse = await callWithRequest('ad.searchTasks', { - body: getLatestTaskForDetectorQuery(detectorId, false), - }); - + body: getLatestTaskForDetectorQuery(detectorId, false), + }); } catch (err) { if (!isIndexNotFoundError(err)) { throw err; @@ -437,7 +458,7 @@ export default class AdService { ): Promise> => { try { const { detectorId } = request.params as { detectorId: string }; - const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + const { dataSourceId = '' } = request.params as { dataSourceId?: string }; //@ts-ignore const startTime = request.body?.startTime; //@ts-ignore @@ -457,11 +478,12 @@ export default class AdService { } const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client); + this.client + ); const response = await callWithRequest(requestPath, requestParams); @@ -492,7 +514,7 @@ export default class AdService { detectorId: string; isHistorical: any; }; - const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + const { dataSourceId = '' } = request.params as { dataSourceId?: string }; isHistorical = JSON.parse(isHistorical) as boolean; const requestPath = isHistorical @@ -500,16 +522,16 @@ export default class AdService { : 'ad.stopDetector'; const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client); + this.client + ); - const response = await callWithRequest( - requestPath, { - detectorId, - }); + const response = await callWithRequest(requestPath, { + detectorId, + }); return opensearchDashboardsResponse.ok({ body: { @@ -609,7 +631,7 @@ export default class AdService { resultIndex: string; onlyQueryCustomResultIndex: boolean; }; - const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + const { dataSourceId = '' } = request.params as { dataSourceId?: string }; if ( !resultIndex || @@ -625,20 +647,21 @@ export default class AdService { const requestBody = JSON.stringify(request.body); const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client); + this.client + ); const response = !resultIndex ? await callWithRequest('ad.searchResults', { - body: requestBody, - }) + body: requestBody, + }) : await callWithRequest('ad.searchResultsFromCustomResultIndex', { - ...requestParams, - body: requestBody, - }); + ...requestParams, + body: requestBody, + }); return opensearchDashboardsResponse.ok({ body: { ok: true, @@ -674,7 +697,7 @@ export default class AdService { indices = '', sortDirection = SORT_DIRECTION.DESC, sortField = 'name', - dataSourceId = '' + dataSourceId = '', } = request.query as GetDetectorsQueryParams; const mustQueries = []; if (search.trim()) { @@ -719,12 +742,15 @@ export default class AdService { }; const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client); - const response = await callWithRequest('ad.searchDetector', { body: requestBody }); + this.client + ); + const response = await callWithRequest('ad.searchDetector', { + body: requestBody, + }); const totalDetectors = get(response, 'hits.total.value', 0); @@ -758,7 +784,8 @@ export default class AdService { } as {}; const aggregationResult = await callWithRequest( - 'ad.searchResultsFromCustomResultIndex', { + 'ad.searchResultsFromCustomResultIndex', + { ...requestParams, body: getResultAggregationQuery(allDetectorIds, { from, @@ -768,7 +795,8 @@ export default class AdService { search, indices, }), - }); + } + ); const aggsDetectors = get( aggregationResult, 'aggregations.unique_detectors.buckets', @@ -821,15 +849,12 @@ export default class AdService { let realtimeTasksResponse = {} as any; let historicalTasksResponse = {} as any; try { - realtimeTasksResponse = await callWithRequest( - 'ad.searchTasks', { - body: getLatestDetectorTasksQuery(true), + realtimeTasksResponse = await callWithRequest('ad.searchTasks', { + body: getLatestDetectorTasksQuery(true), }); - historicalTasksResponse = await callWithRequest( - 'ad.searchTasks', { + historicalTasksResponse = await callWithRequest('ad.searchTasks', { body: getLatestDetectorTasksQuery(false), }); - } catch (err) { if (!isIndexNotFoundError(err)) { throw err; @@ -916,7 +941,7 @@ export default class AdService { resultIndex: string; onlyQueryCustomResultIndex: boolean; }; - const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + const { dataSourceId = '' } = request.params as { dataSourceId?: string }; if ( !resultIndex || @@ -1050,23 +1075,22 @@ export default class AdService { } as {}; const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client); + this.client + ); const response = !resultIndex - ? await callWithRequest( - 'ad.searchResults', { - body: requestBody, - }) - : await callWithRequest( - 'ad.searchResultsFromCustomResultIndex', { - ...requestParams, - body: requestBody, - }); - + ? await callWithRequest('ad.searchResults', { + body: requestBody, + }) + : await callWithRequest('ad.searchResultsFromCustomResultIndex', { + ...requestParams, + body: requestBody, + }); + const totalResults: number = get(response, 'hits.total.value', 0); const detectorResult: AnomalyResult[] = []; @@ -1154,26 +1178,25 @@ export default class AdService { detectorId: string; isHistorical: any; }; - const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + const { dataSourceId = '' } = request.params as { dataSourceId?: string }; isHistorical = JSON.parse(isHistorical) as boolean; const requestPath = isHistorical ? 'ad.topHistoricalAnomalyResults' : 'ad.topAnomalyResults'; - const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client); + this.client + ); - const response = await callWithRequest( - requestPath, { - detectorId: detectorId, - body: request.body, - }); + const response = await callWithRequest(requestPath, { + detectorId: detectorId, + body: request.body, + }); return opensearchDashboardsResponse.ok({ body: { @@ -1276,4 +1299,4 @@ export default class AdService { } return expectedValue; }; -} \ No newline at end of file +} diff --git a/server/routes/alerting.ts b/server/routes/alerting.ts index 0da07a72..c0728fb5 100644 --- a/server/routes/alerting.ts +++ b/server/routes/alerting.ts @@ -29,7 +29,10 @@ export function registerAlertingRoutes( alertingService: AlertingService ) { apiRouter.post('/monitors/_search', alertingService.searchMonitors); - apiRouter.post('/monitors/_search/{dataSourceId}', alertingService.searchMonitors); + apiRouter.post( + '/monitors/_search/{dataSourceId}', + alertingService.searchMonitors + ); apiRouter.get('/monitors/alerts', alertingService.searchAlerts); } @@ -49,7 +52,7 @@ export default class AlertingService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { try { - const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + const { dataSourceId = '' } = request.params as { dataSourceId?: string }; const requestBody = { size: MAX_MONITORS, @@ -80,14 +83,17 @@ export default class AlertingService { }; const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client); + this.client + ); const response: SearchResponse = await callWithRequest( - 'alerting.searchMonitors', { body: requestBody }); + 'alerting.searchMonitors', + { body: requestBody } + ); const totalMonitors = get(response, 'hits.total.value', 0); const allMonitors = get(response, 'hits.hits', []).reduce( (acc: any, monitor: any) => ({ @@ -160,4 +166,4 @@ export default class AlertingService { }); } }; -} \ No newline at end of file +} diff --git a/server/routes/opensearch.ts b/server/routes/opensearch.ts index cdc647d2..622db995 100644 --- a/server/routes/opensearch.ts +++ b/server/routes/opensearch.ts @@ -124,21 +124,21 @@ export default class OpenSearchService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { const { index } = request.query as { index: string }; - const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + const { dataSourceId = '' } = request.params as { dataSourceId?: string }; try { const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client); + this.client + ); - const response: CatIndex[] = await callWithRequest( - 'cat.indices', { - index, - format: 'json', - h: 'health,index', - }); + const response: CatIndex[] = await callWithRequest('cat.indices', { + index, + format: 'json', + h: 'health,index', + }); return opensearchDashboardsResponse.ok({ body: { ok: true, response: { indices: response } }, @@ -169,22 +169,22 @@ export default class OpenSearchService { opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { const { alias } = request.query as { alias: string }; - const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + const { dataSourceId = '' } = request.params as { dataSourceId?: string }; try { const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client); + this.client + ); - const response: IndexAlias[] = await callWithRequest( - 'cat.aliases', { - alias, - format: 'json', - h: 'alias,index', - }); + const response: IndexAlias[] = await callWithRequest('cat.aliases', { + alias, + format: 'json', + h: 'alias,index', + }); return opensearchDashboardsResponse.ok({ body: { ok: true, response: { aliases: response } }, }); @@ -204,18 +204,19 @@ export default class OpenSearchService { request: OpenSearchDashboardsRequest, opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { - const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + const { dataSourceId = '' } = request.params as { dataSourceId?: string }; //@ts-ignore const index = request.body.index; //@ts-ignore const body = request.body.body; const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client); + this.client + ); try { await callWithRequest('indices.create', { index: index, @@ -232,10 +233,10 @@ export default class OpenSearchService { } try { const response: CatIndex[] = await callWithRequest('cat.indices', { - index, - format: 'json', - h: 'health,index', - }); + index, + format: 'json', + h: 'health,index', + }); return opensearchDashboardsResponse.ok({ body: { ok: true, response: { indices: response } }, }); @@ -255,19 +256,20 @@ export default class OpenSearchService { request: OpenSearchDashboardsRequest, opensearchDashboardsResponse: OpenSearchDashboardsResponseFactory ): Promise> => { - const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + const { dataSourceId = '' } = request.params as { dataSourceId?: string }; const body = request.body; try { const callWithRequest = getClientBasedOnDataSource( - context, - this.dataSourceEnabled, - request, + context, + this.dataSourceEnabled, + request, dataSourceId, - this.client); + this.client + ); const response: any = await callWithRequest('bulk', { - body: body, - }); + body: body, + }); return opensearchDashboardsResponse.ok({ body: { ok: true, response: { response } }, }); diff --git a/server/routes/sampleData.ts b/server/routes/sampleData.ts index 303ce486..5e8141a3 100644 --- a/server/routes/sampleData.ts +++ b/server/routes/sampleData.ts @@ -27,10 +27,14 @@ export function registerSampleDataRoutes( apiRouter: Router, sampleDataService: SampleDataService ) { - apiRouter.post('/create_sample_data/{type}', - sampleDataService.createSampleData); - apiRouter.post('/create_sample_data/{type}/{dataSourceId}', - sampleDataService.createSampleData); + apiRouter.post( + '/create_sample_data/{type}', + sampleDataService.createSampleData + ); + apiRouter.post( + '/create_sample_data/{type}/{dataSourceId}', + sampleDataService.createSampleData + ); } export default class SampleDataService { @@ -81,7 +85,14 @@ export default class SampleDataService { } } - await loadSampleData(filePath, indexName, this.client, request, context, this.dataSourceEnabled); + await loadSampleData( + filePath, + indexName, + this.client, + request, + context, + this.dataSourceEnabled + ); return opensearchDashboardsResponse.ok({ body: { ok: true } }); } catch (err) { diff --git a/server/sampleData/utils/helpers.ts b/server/sampleData/utils/helpers.ts index 42a37eba..e53f6c8f 100644 --- a/server/sampleData/utils/helpers.ts +++ b/server/sampleData/utils/helpers.ts @@ -19,7 +19,10 @@ import { import fs from 'fs'; import { createUnzip } from 'zlib'; import { isEmpty } from 'lodash'; -import { getClientBasedOnDataSource, prettifyErrorMessage } from '../../utils/helpers'; +import { + getClientBasedOnDataSource, + prettifyErrorMessage, +} from '../../utils/helpers'; const BULK_INSERT_SIZE = 500; @@ -31,7 +34,7 @@ export const loadSampleData = ( context: RequestHandlerContext, dataSourceEnabled: boolean ) => { - const { dataSourceId = "" } = request.params as { dataSourceId?: string }; + const { dataSourceId = '' } = request.params as { dataSourceId?: string }; return new Promise((resolve, reject) => { let count: number = 0; @@ -100,11 +103,12 @@ export const loadSampleData = ( }); const callWithRequest = getClientBasedOnDataSource( - context, - dataSourceEnabled, - request, + context, + dataSourceEnabled, + request, dataSourceId, - client); + client + ); const bulkInsert = async (docs: any[]) => { try { diff --git a/server/utils/helpers.ts b/server/utils/helpers.ts index 6233a007..4795e701 100644 --- a/server/utils/helpers.ts +++ b/server/utils/helpers.ts @@ -21,8 +21,10 @@ import { import { MIN_IN_MILLI_SECS } from './constants'; import { + ILegacyClusterClient, LegacyCallAPIOptions, OpenSearchDashboardsRequest, + RequestHandlerContext, } from '../../../../src/core/server'; export const SHOW_DECIMAL_NUMBER_THRESHOLD = 0.01; @@ -87,11 +89,11 @@ export const prettifyErrorMessage = (rawErrorMessage: string) => { }; export function getClientBasedOnDataSource( - context: any, + context: RequestHandlerContext, dataSourceEnabled: boolean, request: OpenSearchDashboardsRequest, dataSourceId: string, - client: any + client: ILegacyClusterClient | undefined ): ( endpoint: string, clientParams?: Record, @@ -101,6 +103,9 @@ export function getClientBasedOnDataSource( // client for remote cluster return context.dataSource.opensearch.legacy.getClient(dataSourceId).callAPI; } else { + if (client === undefined) { + throw new Error('Client cannot be undefined.'); + } // fall back to default local cluster return client.asScoped(request).callAsCurrentUser; } From e28ff14a2ae58cfad92cb6afdc5b0e00f83461aa Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Wed, 17 Apr 2024 17:57:59 -0700 Subject: [PATCH 23/26] addressing comments Signed-off-by: Jackie Han --- public/pages/main/Main.tsx | 24 ++++++++++-------------- public/pages/utils/helpers.ts | 4 ++-- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/public/pages/main/Main.tsx b/public/pages/main/Main.tsx index d3848abf..292270c6 100644 --- a/public/pages/main/Main.tsx +++ b/public/pages/main/Main.tsx @@ -65,28 +65,24 @@ export function Main(props: MainProps) { return `#${basePath}?${searchParams.toString()}`; }; - // Usage examples: - const dashboardHref = constructHrefWithDataSourceId( - APP_PATH.DASHBOARD, - '', - dataSourceId - ); - const overviewHref = constructHrefWithDataSourceId( - APP_PATH.OVERVIEW, - '', - dataSourceId - ); - const sideNav = [ { name: Navigation.AnomalyDetection, id: 0, - href: overviewHref, + href: constructHrefWithDataSourceId( + APP_PATH.OVERVIEW, + '', + dataSourceId + ), items: [ { name: Navigation.Dashboard, id: 1, - href: dashboardHref, + href: constructHrefWithDataSourceId( + APP_PATH.DASHBOARD, + '', + dataSourceId + ), isSelected: props.location.pathname === APP_PATH.DASHBOARD, }, { diff --git a/public/pages/utils/helpers.ts b/public/pages/utils/helpers.ts index 23f8d5d8..f5c0fcfe 100644 --- a/public/pages/utils/helpers.ts +++ b/public/pages/utils/helpers.ts @@ -122,7 +122,7 @@ export const getAllDetectorsQueryParamsWithDataSourceId = ( size: MAX_DETECTORS, sortDirection: SORT_DIRECTION.ASC, sortField: 'name', - dataSourceId: dataSourceId, + dataSourceId }); export const getSampleDetectorsQueryParamsWithDataSouceId = ( @@ -134,5 +134,5 @@ export const getSampleDetectorsQueryParamsWithDataSouceId = ( size: MAX_DETECTORS, sortDirection: SORT_DIRECTION.ASC, sortField: 'name', - dataSourceId: dataSourceId, + dataSourceId }); From 598e3d3cd989972db6a4e29388d10a7d6f480680 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Wed, 17 Apr 2024 18:01:11 -0700 Subject: [PATCH 24/26] make getDataSourceManagementPlugin() optional Signed-off-by: Jackie Han --- public/pages/Dashboard/Container/DashboardOverview.tsx | 2 +- public/pages/DetectorDetail/containers/DetectorDetail.tsx | 2 +- public/pages/DetectorsList/containers/List/List.tsx | 2 +- public/pages/Overview/containers/AnomalyDetectionOverview.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/pages/Dashboard/Container/DashboardOverview.tsx b/public/pages/Dashboard/Container/DashboardOverview.tsx index 46ed3307..88e90eda 100644 --- a/public/pages/Dashboard/Container/DashboardOverview.tsx +++ b/public/pages/Dashboard/Container/DashboardOverview.tsx @@ -265,7 +265,7 @@ export function DashboardOverview(props: OverviewProps) { let renderDataSourceComponent = null; if (dataSourceEnabled) { const DataSourceMenu = - getDataSourceManagementPlugin().ui.getDataSourceMenu(); + getDataSourceManagementPlugin()?.ui.getDataSourceMenu(); renderDataSourceComponent = useMemo(() => { return ( { let renderDataSourceComponent = null; if (dataSourceEnabled) { const DataSourceMenu = - getDataSourceManagementPlugin().ui.getDataSourceMenu(); + getDataSourceManagementPlugin()?.ui.getDataSourceMenu(); renderDataSourceComponent = ( { let renderDataSourceComponent = null; if (dataSourceEnabled) { const DataSourceMenu = - getDataSourceManagementPlugin().ui.getDataSourceMenu(); + getDataSourceManagementPlugin()?.ui.getDataSourceMenu(); renderDataSourceComponent = useMemo(() => { return ( (); + getDataSourceManagementPlugin()?.ui.getDataSourceMenu(); renderDataSourceComponent = useMemo(() => { return ( Date: Wed, 17 Apr 2024 18:09:14 -0700 Subject: [PATCH 25/26] add comment Signed-off-by: Jackie Han --- server/plugin.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/plugin.ts b/server/plugin.ts index 5736a3bf..2c925ffd 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -67,6 +67,8 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin const dataSourceEnabled = !!dataSource; + // when MDS is enabled, we leave the client as undefined for now + // it will be defined later with dataSourceId when we have request context let client: ILegacyClusterClient | undefined = undefined; if (!dataSourceEnabled) { From 139d31549035e6b6cfd4f3623344babd8ae69bcd Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Wed, 17 Apr 2024 22:55:26 -0700 Subject: [PATCH 26/26] make dataSourceId type safe Signed-off-by: Jackie Han --- public/pages/Dashboard/utils/utils.tsx | 6 +++--- .../DetectorDetail/hooks/useFetchMonitorInfo.ts | 2 +- public/pages/main/Main.tsx | 6 +++--- public/redux/reducers/ad.ts | 14 +++++++------- public/redux/reducers/alerting.ts | 2 +- public/redux/reducers/anomalyResults.ts | 4 ++-- public/redux/reducers/opensearch.ts | 10 +++++----- public/redux/reducers/sampleData.ts | 2 +- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/public/pages/Dashboard/utils/utils.tsx b/public/pages/Dashboard/utils/utils.tsx index 188cbab9..86414003 100644 --- a/public/pages/Dashboard/utils/utils.tsx +++ b/public/pages/Dashboard/utils/utils.tsx @@ -443,7 +443,7 @@ export const getLatestAnomalyResultsByTimeRange = async ( checkLastIndexOnly: boolean, resultIndex: string, onlyQueryCustomResultIndex: boolean, - dataSourceId = '' + dataSourceId: string ): Promise => { let from = 0; let anomalyResults = [] as object[]; @@ -504,7 +504,7 @@ export const getLatestAnomalyResultsForDetectorsByTimeRange = async ( checkLastIndexOnly: boolean, resultIndex: string, onlyQueryCustomResultIndex: boolean, - dataSourceId = '' + dataSourceId: string ): Promise => { const detectorAndIdMap = buildDetectorAndIdMap(selectedDetectors); let from = 0; @@ -615,7 +615,7 @@ export const getAnomalyDistributionForDetectorsByTimeRange = async ( onlyQueryCustomResultIndex: boolean ) => APIAction, selectedDetectors: DetectorListItem[], - dataSourceId = '', + dataSourceId: string, timeRange: string, dispatch: Dispatch, threshold: number, diff --git a/public/pages/DetectorDetail/hooks/useFetchMonitorInfo.ts b/public/pages/DetectorDetail/hooks/useFetchMonitorInfo.ts index 68938968..220f0570 100644 --- a/public/pages/DetectorDetail/hooks/useFetchMonitorInfo.ts +++ b/public/pages/DetectorDetail/hooks/useFetchMonitorInfo.ts @@ -19,7 +19,7 @@ import { searchMonitors } from '../../../redux/reducers/alerting'; //A hook which gets AD monitor. export const useFetchMonitorInfo = ( detectorId: string, - dataSourceId = '', + dataSourceId: string, dataSourceEnabled: boolean ): { monitor: Monitor | undefined; diff --git a/public/pages/main/Main.tsx b/public/pages/main/Main.tsx index 292270c6..4fc9a0be 100644 --- a/public/pages/main/Main.tsx +++ b/public/pages/main/Main.tsx @@ -52,9 +52,9 @@ export function Main(props: MainProps) { 'from=0&size=20&search=&indices=&sortField=name&sortDirection=asc'; const constructHrefWithDataSourceId = ( - basePath, - existingParams = '', - dataSourceId = '' + basePath: string, + existingParams: string, + dataSourceId: string ) => { const searchParams = new URLSearchParams(existingParams); diff --git a/public/redux/reducers/ad.ts b/public/redux/reducers/ad.ts index ed0aea0f..67e7b558 100644 --- a/public/redux/reducers/ad.ts +++ b/public/redux/reducers/ad.ts @@ -371,7 +371,7 @@ const reducer = handleActions( export const createDetector = ( requestBody: Detector, - dataSourceId = '' + dataSourceId: string ): APIAction => { const url = dataSourceId ? `..${AD_NODE_API.DETECTOR}/${dataSourceId}` @@ -399,7 +399,7 @@ export const validateDetector = ( export const getDetector = ( detectorId: string, - dataSourceId = '' + dataSourceId: string ): APIAction => { const baseUrl = `..${AD_NODE_API.DETECTOR}`; const url = dataSourceId @@ -449,7 +449,7 @@ export const updateDetector = ( export const deleteDetector = ( detectorId: string, - dataSourceId = '' + dataSourceId: string ): APIAction => { const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; @@ -463,7 +463,7 @@ export const deleteDetector = ( export const startDetector = ( detectorId: string, - dataSourceId = '' + dataSourceId: string ): APIAction => { const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}/start`; const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; @@ -477,7 +477,7 @@ export const startDetector = ( export const startHistoricalDetector = ( detectorId: string, - dataSourceId = '', + dataSourceId: string, startTime: number, endTime: number ): APIAction => { @@ -503,7 +503,7 @@ export const startHistoricalDetector = ( export const stopDetector = ( detectorId: string, - dataSourceId = '' + dataSourceId: string ): APIAction => { const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}/stop/${false}`; const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; @@ -517,7 +517,7 @@ export const stopDetector = ( export const stopHistoricalDetector = ( detectorId: string, - dataSourceId = '' + dataSourceId: string ): APIAction => { const baseUrl = `..${AD_NODE_API.DETECTOR}/${detectorId}`; const url = dataSourceId diff --git a/public/redux/reducers/alerting.ts b/public/redux/reducers/alerting.ts index ece38e43..79bfb3c9 100644 --- a/public/redux/reducers/alerting.ts +++ b/public/redux/reducers/alerting.ts @@ -94,7 +94,7 @@ const reducer = handleActions( initialDetectorsState ); -export const searchMonitors = (dataSourceId = ''): APIAction => { +export const searchMonitors = (dataSourceId: string): APIAction => { const baseUrl = `..${ALERTING_NODE_API._SEARCH}`; const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; diff --git a/public/redux/reducers/anomalyResults.ts b/public/redux/reducers/anomalyResults.ts index 845e3bb4..5eda008e 100644 --- a/public/redux/reducers/anomalyResults.ts +++ b/public/redux/reducers/anomalyResults.ts @@ -94,7 +94,7 @@ const reducer = handleActions( export const getDetectorResults = ( id: string, - dataSourceId = '', + dataSourceId: string, queryParams: any, isHistorical: boolean, resultIndex: string, @@ -119,7 +119,7 @@ export const getDetectorResults = ( export const searchResults = ( requestBody: any, resultIndex: string, - dataSourceId = '', + dataSourceId: string, onlyQueryCustomResultIndex: boolean ): APIAction => { let baseUrl = `..${AD_NODE_API.DETECTOR}/results/_search`; diff --git a/public/redux/reducers/opensearch.ts b/public/redux/reducers/opensearch.ts index afd095ca..0c354c45 100644 --- a/public/redux/reducers/opensearch.ts +++ b/public/redux/reducers/opensearch.ts @@ -246,7 +246,7 @@ const reducer = handleActions( initialState ); -export const getIndices = (searchKey = '', dataSourceId = '') => { +export const getIndices = (searchKey = '', dataSourceId: string) => { const baseUrl = `..${AD_NODE_API._INDICES}`; const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; @@ -259,7 +259,7 @@ export const getIndices = (searchKey = '', dataSourceId = '') => { export const getAliases = ( searchKey: string = '', - dataSourceId = '' + dataSourceId: string ): APIAction => { const baseUrl = `..${AD_NODE_API._ALIASES}`; const url = dataSourceId ? `${baseUrl}/${dataSourceId}` : baseUrl; @@ -287,7 +287,7 @@ export const searchOpenSearch = (requestData: any): APIAction => ({ }), }); -export const createIndex = (indexConfig: any, dataSourceId = ''): APIAction => { +export const createIndex = (indexConfig: any, dataSourceId: string): APIAction => { const url = dataSourceId ? `${AD_NODE_API.CREATE_INDEX}/${dataSourceId}` : AD_NODE_API.CREATE_INDEX; @@ -300,7 +300,7 @@ export const createIndex = (indexConfig: any, dataSourceId = ''): APIAction => { }; }; -export const bulk = (body: any, dataSourceId = ''): APIAction => { +export const bulk = (body: any, dataSourceId: string): APIAction => { const url = dataSourceId ? `${AD_NODE_API.BULK}/${dataSourceId}` : AD_NODE_API.BULK; @@ -318,7 +318,7 @@ export const deleteIndex = (index: string): APIAction => ({ }); export const getPrioritizedIndices = - (searchKey: string, dataSourceId = ''): ThunkAction => + (searchKey: string, dataSourceId: string): ThunkAction => async (dispatch, getState) => { //Fetch Indices and Aliases with text provided await dispatch(getIndices(searchKey, dataSourceId)); diff --git a/public/redux/reducers/sampleData.ts b/public/redux/reducers/sampleData.ts index a3e19ddd..b92ce773 100644 --- a/public/redux/reducers/sampleData.ts +++ b/public/redux/reducers/sampleData.ts @@ -56,7 +56,7 @@ const reducer = handleActions( export const createSampleData = ( sampleDataType: SAMPLE_TYPE, - dataSourceId = '' + dataSourceId: string ): APIAction => { const url = dataSourceId ? `..${AD_NODE_API.CREATE_SAMPLE_DATA}/${sampleDataType}/${dataSourceId}`