diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json
index 247dfa9..09e8b7c 100644
--- a/opensearch_dashboards.json
+++ b/opensearch_dashboards.json
@@ -5,5 +5,6 @@
"server": true,
"ui": true,
"requiredPlugins": ["navigation"],
- "optionalPlugins": []
+ "optionalPlugins": ["dataSource",
+ "dataSourceManagement"]
}
diff --git a/public/application.tsx b/public/application.tsx
index 70850a8..8c6560e 100644
--- a/public/application.tsx
+++ b/public/application.tsx
@@ -9,18 +9,25 @@ import { HashRouter as Router } from 'react-router-dom';
import { AppMountParameters, CoreStart } from '../../../src/core/public';
import { QueryInsightsDashboardsApp } from './components/app';
import { QueryInsightsDashboardsPluginStartDependencies } from './types';
+import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public';
export const renderApp = (
core: CoreStart,
depsStart: QueryInsightsDashboardsPluginStartDependencies,
- { element }: AppMountParameters
+ params: AppMountParameters,
+ dataSourceManagement?: DataSourceManagementPluginSetup
) => {
ReactDOM.render(
-
+
,
- element
+ params.element
);
- return () => ReactDOM.unmountComponentAtNode(element);
+ return () => ReactDOM.unmountComponentAtNode(params.element);
};
diff --git a/public/components/DataSourcePicker.tsx b/public/components/DataSourcePicker.tsx
new file mode 100644
index 0000000..19ee5e0
--- /dev/null
+++ b/public/components/DataSourcePicker.tsx
@@ -0,0 +1,73 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import {
+ DataSourceManagementPluginSetup,
+ DataSourceOption,
+ DataSourceSelectableConfig,
+} from 'src/plugins/data_source_management/public';
+import { AppMountParameters, CoreStart } from '../../../../src/core/public';
+import { QueryInsightsDashboardsPluginStartDependencies } from '../types';
+
+export interface DataSourceMenuProps {
+ dataSourceManagement: DataSourceManagementPluginSetup;
+ depsStart: QueryInsightsDashboardsPluginStartDependencies;
+ coreStart: CoreStart;
+ params: AppMountParameters;
+ setDataSource: React.Dispatch>;
+ selectedDataSource: DataSourceOption;
+}
+
+export function getDataSourceEnabledUrl(dataSource: DataSourceOption) {
+ const url = new URL(window.location.href);
+ url.searchParams.set('dataSource', JSON.stringify(dataSource));
+ return url;
+}
+
+export function getDataSourceFromUrl(): DataSourceOption {
+ const urlParams = new URLSearchParams(window.location.search);
+ const dataSourceParam = (urlParams && urlParams.get('dataSource')) || '{}';
+ // following block is needed if the dataSource param is set to non-JSON value, say 'undefined'
+ try {
+ return JSON.parse(dataSourceParam);
+ } catch (e) {
+ return JSON.parse('{}'); // Return an empty object or some default value if parsing fails
+ }
+}
+
+export const QueryInsightsDataSourceMenu = React.memo(
+ (props: DataSourceMenuProps) => {
+ const { coreStart, depsStart, dataSourceManagement, params, setDataSource,
+ selectedDataSource } = props;
+ const { setHeaderActionMenu } = params;
+ const DataSourceMenu = dataSourceManagement?.ui.getDataSourceMenu();
+
+ const dataSourceEnabled = !!depsStart.dataSource?.dataSourceEnabled;
+
+ const wrapSetDataSourceWithUpdateUrl = (dataSources: DataSourceOption[]) => {
+ window.history.replaceState({}, '', getDataSourceEnabledUrl(dataSources[0]).toString());
+ setDataSource(dataSources[0]);
+ };
+
+ return dataSourceEnabled ? (
+
+ ) : null;
+ },
+ (prevProps, newProps) =>
+ prevProps.selectedDataSource.id === newProps.selectedDataSource.id &&
+ // prevProps.dataSourcePickerReadOnly === newProps.dataSourcePickerReadOnly
+)
diff --git a/public/components/app.tsx b/public/components/app.tsx
index f526e4f..f8032cd 100644
--- a/public/components/app.tsx
+++ b/public/components/app.tsx
@@ -5,16 +5,32 @@
import React from 'react';
import { Route } from 'react-router-dom';
+import { DataSourceManagementPluginSetup } from 'src/plugins/data_source_management/public';
import TopNQueries from '../pages/TopNQueries/TopNQueries';
-import { CoreStart } from '../../../../src/core/public';
+import { AppMountParameters, CoreStart } from '../../../../src/core/public';
import { QueryInsightsDashboardsPluginStartDependencies } from '../types';
export const QueryInsightsDashboardsApp = ({
core,
depsStart,
+ params,
+ dataSourceManagement,
}: {
core: CoreStart;
depsStart: QueryInsightsDashboardsPluginStartDependencies;
+ params: AppMountParameters;
+ dataSourceManagement?: DataSourceManagementPluginSetup;
}) => {
- return } />;
+ return (
+ (
+
+ )}
+ />
+ );
};
diff --git a/public/pages/Configuration/Configuration.test.tsx b/public/pages/Configuration/Configuration.test.tsx
index b235b57..fc6746a 100644
--- a/public/pages/Configuration/Configuration.test.tsx
+++ b/public/pages/Configuration/Configuration.test.tsx
@@ -49,6 +49,8 @@ const renderConfiguration = (overrides = {}) =>
groupBySettings={groupBySettings}
configInfo={mockConfigInfo}
core={mockCoreStart}
+ depsStart={{ navigation: {} }}
+ params={{} as any}
/>
);
diff --git a/public/pages/Configuration/Configuration.tsx b/public/pages/Configuration/Configuration.tsx
index 47e099a..93baa7d 100644
--- a/public/pages/Configuration/Configuration.tsx
+++ b/public/pages/Configuration/Configuration.tsx
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useCallback, useState, useEffect } from 'react';
+import React, {useCallback, useState, useEffect, useContext} from 'react';
import {
EuiBottomBar,
EuiButton,
@@ -23,14 +23,17 @@ import {
EuiTitle,
} from '@elastic/eui';
import { useHistory, useLocation } from 'react-router-dom';
-import { CoreStart } from 'opensearch-dashboards/public';
-import { QUERY_INSIGHTS, MetricSettings, GroupBySettings } from '../TopNQueries/TopNQueries';
+import { AppMountParameters, CoreStart } from 'opensearch-dashboards/public';
+import { DataSourceManagementPluginSetup } from 'src/plugins/data_source_management/public';
+import {QUERY_INSIGHTS, MetricSettings, GroupBySettings, DataSourceContext} from '../TopNQueries/TopNQueries';
import {
METRIC_TYPES_TEXT,
TIME_UNITS_TEXT,
MINUTES_OPTIONS,
GROUP_BY_OPTIONS,
} from '../Utils/Constants';
+import { QueryInsightsDataSourceMenu } from '../../components/DataSourcePicker';
+import { QueryInsightsDashboardsPluginStartDependencies } from '../../types';
const Configuration = ({
latencySettings,
@@ -39,6 +42,9 @@ const Configuration = ({
groupBySettings,
configInfo,
core,
+ depsStart,
+ params,
+ dataSourceManagement,
}: {
latencySettings: MetricSettings;
cpuSettings: MetricSettings;
@@ -46,6 +52,9 @@ const Configuration = ({
groupBySettings: GroupBySettings;
configInfo: any;
core: CoreStart;
+ params: AppMountParameters;
+ dataSourceManagement?: DataSourceManagementPluginSetup;
+ depsStart: QueryInsightsDashboardsPluginStartDependencies;
}) => {
const history = useHistory();
const location = useLocation();
@@ -56,6 +65,7 @@ const Configuration = ({
const [windowSize, setWindowSize] = useState(latencySettings.currWindowSize);
const [time, setTime] = useState(latencySettings.currTimeUnit);
const [groupBy, setGroupBy] = useState(groupBySettings.groupBy);
+ const { dataSource, setDataSource } = useContext(DataSourceContext)!;
const [metricSettingsMap, setMetricSettingsMap] = useState({
latency: latencySettings,
@@ -170,6 +180,14 @@ const Configuration = ({
return (
+
diff --git a/public/pages/QueryDetails/QueryDetails.test.tsx b/public/pages/QueryDetails/QueryDetails.test.tsx
index a545fca..516c36e 100644
--- a/public/pages/QueryDetails/QueryDetails.test.tsx
+++ b/public/pages/QueryDetails/QueryDetails.test.tsx
@@ -49,7 +49,7 @@ describe('QueryDetails component', () => {
]}
>
-
+
);
diff --git a/public/pages/QueryDetails/QueryDetails.tsx b/public/pages/QueryDetails/QueryDetails.tsx
index 4ecb2d5..6206a10 100644
--- a/public/pages/QueryDetails/QueryDetails.tsx
+++ b/public/pages/QueryDetails/QueryDetails.tsx
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useCallback, useEffect, useState } from 'react';
+import React, { useCallback, useContext, useEffect, useState } from 'react';
// @ts-ignore
import Plotly from 'plotly.js-dist';
import {
@@ -19,19 +19,25 @@ import {
EuiTitle,
} from '@elastic/eui';
import { useHistory, useLocation } from 'react-router-dom';
-import { CoreStart } from 'opensearch-dashboards/public';
+import { AppMountParameters, CoreStart } from 'opensearch-dashboards/public';
+import { DataSourceManagementPluginSetup } from 'src/plugins/data_source_management/public';
import QuerySummary from './Components/QuerySummary';
-import { QUERY_INSIGHTS } from '../TopNQueries/TopNQueries';
+import { DataSourceContext, QUERY_INSIGHTS } from '../TopNQueries/TopNQueries';
import { SearchQueryRecord } from '../../../types/types';
import { PageHeader } from '../../components/PageHeader';
import { QueryInsightsDashboardsPluginStartDependencies } from '../../types';
import { retrieveQueryById } from '../Utils/QueryUtils';
+import { getDataSourceFromUrl, QueryInsightsDataSourceMenu } from '../../components/DataSourcePicker';
const QueryDetails = ({
core,
depsStart,
+ params,
+ dataSourceManagement,
}: {
core: CoreStart;
+ params: AppMountParameters;
+ dataSourceManagement?: DataSourceManagementPluginSetup;
depsStart: QueryInsightsDashboardsPluginStartDependencies;
}) => {
// Get url parameters
@@ -43,6 +49,7 @@ const QueryDetails = ({
const [query, setQuery] = useState(null);
const history = useHistory();
+ const { dataSource, setDataSource } = useContext(DataSourceContext)!;
// Convert UNIX time to a readable format
const convertTime = useCallback((unixTime: number) => {
@@ -53,7 +60,7 @@ const QueryDetails = ({
useEffect(() => {
const fetchQueryDetails = async () => {
- const retrievedQuery = await retrieveQueryById(core, from, to, id);
+ const retrievedQuery = await retrieveQueryById(core, getDataSourceFromUrl().id, from, to, id);
setQuery(retrievedQuery);
};
@@ -134,7 +141,14 @@ const QueryDetails = ({
>
}
/>
-
+
diff --git a/public/pages/QueryGroupDetails/QueryGroupDetails.test.tsx b/public/pages/QueryGroupDetails/QueryGroupDetails.test.tsx
index db49f37..a85d92c 100644
--- a/public/pages/QueryGroupDetails/QueryGroupDetails.test.tsx
+++ b/public/pages/QueryGroupDetails/QueryGroupDetails.test.tsx
@@ -52,7 +52,7 @@ describe('QueryGroupDetails', () => {
initialEntries={['/query-group-details?id=mockId&from=1632441600000&to=1632528000000']}
>
-
+
);
diff --git a/public/pages/QueryGroupDetails/QueryGroupDetails.tsx b/public/pages/QueryGroupDetails/QueryGroupDetails.tsx
index f679815..3e42cd4 100644
--- a/public/pages/QueryGroupDetails/QueryGroupDetails.tsx
+++ b/public/pages/QueryGroupDetails/QueryGroupDetails.tsx
@@ -3,11 +3,12 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { CoreStart } from 'opensearch-dashboards/public';
+import { AppMountParameters, CoreStart } from 'opensearch-dashboards/public';
+import { DataSourceManagementPluginSetup } from 'src/plugins/data_source_management/public';
// @ts-ignore
import Plotly from 'plotly.js-dist';
import { useHistory, useLocation } from 'react-router-dom';
-import React, { useEffect, useState } from 'react';
+import React, {useContext, useEffect, useState} from 'react';
import {
EuiButton,
EuiCodeBlock,
@@ -21,7 +22,7 @@ import {
EuiTitle,
EuiIconTip,
} from '@elastic/eui';
-import { QUERY_INSIGHTS } from '../TopNQueries/TopNQueries';
+import {DataSourceContext, QUERY_INSIGHTS} from '../TopNQueries/TopNQueries';
import { QueryGroupAggregateSummary } from './Components/QueryGroupAggregateSummary';
import { QueryGroupSampleQuerySummary } from './Components/QueryGroupSampleQuerySummary';
@@ -29,12 +30,17 @@ import { QueryInsightsDashboardsPluginStartDependencies } from '../../types';
import { PageHeader } from '../../components/PageHeader';
import { SearchQueryRecord } from '../../../types/types';
import { retrieveQueryById } from '../Utils/QueryUtils';
+import { QueryInsightsDataSourceMenu } from '../../components/DataSourcePicker';
export const QueryGroupDetails = ({
core,
depsStart,
+ params,
+ dataSourceManagement,
}: {
core: CoreStart;
+ params: AppMountParameters;
+ dataSourceManagement?: DataSourceManagementPluginSetup;
depsStart: QueryInsightsDashboardsPluginStartDependencies;
}) => {
// Get url parameters
@@ -45,6 +51,7 @@ export const QueryGroupDetails = ({
const to = searchParams.get('to');
const [query, setQuery] = useState(null);
+ const { dataSource, setDataSource } = useContext(DataSourceContext)!;
const convertTime = (unixTime: number) => {
const date = new Date(unixTime);
@@ -56,7 +63,7 @@ export const QueryGroupDetails = ({
useEffect(() => {
const fetchQueryDetails = async () => {
- const retrievedQuery = await retrieveQueryById(core, from, to, id);
+ const retrievedQuery = await retrieveQueryById(core, '738ffbd0-d8de-11ef-9d96-eff1abd421b8', from, to, id);
setQuery(retrievedQuery);
};
@@ -149,6 +156,14 @@ export const QueryGroupDetails = ({
>
}
/>
+
diff --git a/public/pages/QueryInsights/QueryInsights.test.tsx b/public/pages/QueryInsights/QueryInsights.test.tsx
index d6708e3..3df1201 100644
--- a/public/pages/QueryInsights/QueryInsights.test.tsx
+++ b/public/pages/QueryInsights/QueryInsights.test.tsx
@@ -32,6 +32,8 @@ const renderQueryInsights = () =>
currEnd="now"
// @ts-ignore
core={mockCore}
+ depsStart={{ navigation: {} }}
+ params={{} as any}
/>
);
diff --git a/public/pages/QueryInsights/QueryInsights.tsx b/public/pages/QueryInsights/QueryInsights.tsx
index c4ec1ae..831ed17 100644
--- a/public/pages/QueryInsights/QueryInsights.tsx
+++ b/public/pages/QueryInsights/QueryInsights.tsx
@@ -3,11 +3,12 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useEffect, useState } from 'react';
+import React, {useContext, useEffect, useState} from 'react';
import { EuiBasicTableColumn, EuiInMemoryTable, EuiLink, EuiSuperDatePicker } from '@elastic/eui';
import { useHistory, useLocation } from 'react-router-dom';
-import { CoreStart } from 'opensearch-dashboards/public';
-import { QUERY_INSIGHTS } from '../TopNQueries/TopNQueries';
+import { AppMountParameters, CoreStart } from 'opensearch-dashboards/public';
+import { DataSourceManagementPluginSetup } from 'src/plugins/data_source_management/public';
+import {DataSourceContext, QUERY_INSIGHTS} from '../TopNQueries/TopNQueries';
import { SearchQueryRecord } from '../../../types/types';
import {
CPU_TIME,
@@ -24,6 +25,8 @@ import {
} from '../../../common/constants';
import { calculateMetric } from '../Utils/MetricUtils';
import { parseDateString } from '../Utils/DateUtils';
+import { QueryInsightsDataSourceMenu } from '../../components/DataSourcePicker';
+import { QueryInsightsDashboardsPluginStartDependencies } from '../../types';
const TIMESTAMP_FIELD = 'timestamp';
const MEASUREMENTS_FIELD = 'measurements';
@@ -42,6 +45,9 @@ const QueryInsights = ({
currStart,
currEnd,
core,
+ depsStart,
+ params,
+ dataSourceManagement,
}: {
queries: SearchQueryRecord[];
loading: boolean;
@@ -50,6 +56,9 @@ const QueryInsights = ({
currStart: string;
currEnd: string;
core: CoreStart;
+ params: AppMountParameters;
+ dataSourceManagement?: DataSourceManagementPluginSetup;
+ depsStart: QueryInsightsDashboardsPluginStartDependencies;
}) => {
const history = useHistory();
const location = useLocation();
@@ -57,6 +66,7 @@ const QueryInsights = ({
const from = parseDateString(currStart);
const to = parseDateString(currEnd);
+ const { dataSource, setDataSource } = useContext(DataSourceContext)!;
useEffect(() => {
core.chrome.setBreadcrumbs([
@@ -258,110 +268,120 @@ const QueryInsights = ({
);
return (
- setPagination({ pageIndex: index })}
- pagination={pagination}
- loading={loading}
- search={{
- box: {
- placeholder: 'Search queries',
- schema: false,
- },
- filters: [
- {
- type: 'field_value_selection',
- field: GROUP_BY_FIELD,
- name: TYPE,
- multiSelect: true,
- options: [
- {
- value: 'NONE',
- name: 'query',
- view: 'query',
- },
- {
- value: 'SIMILARITY',
- name: 'group',
- view: 'group',
- },
- ],
- noOptionsMessage: 'No data available for the selected type', // Fallback message when no queries match
- },
- {
- type: 'field_value_selection',
- field: INDICES_FIELD,
- name: INDICES,
- multiSelect: true,
- options: filterDuplicates(
- queries.map((query) => {
- const values = Array.from(new Set(query[INDICES_FIELD].flat()));
- return {
- value: values.join(','),
- name: values.join(','),
- view: values.join(', '),
- };
- })
- ),
- },
- {
- type: 'field_value_selection',
- field: SEARCH_TYPE_FIELD,
- name: SEARCH_TYPE,
- multiSelect: false,
- options: filterDuplicates(
- queries.map((query) => ({
- value: query[SEARCH_TYPE_FIELD],
- name: query[SEARCH_TYPE_FIELD],
- view: query[SEARCH_TYPE_FIELD],
- }))
- ),
+ <>
+
+ ({
- value: query[NODE_ID_FIELD],
- name: query[NODE_ID_FIELD],
- view: query[NODE_ID_FIELD].replaceAll('_', ' '),
- }))
- ),
+ }}
+ onTableChange={({ page: { index } }) => setPagination({ pageIndex: index })}
+ pagination={pagination}
+ loading={loading}
+ search={{
+ box: {
+ placeholder: 'Search queries',
+ schema: false,
},
- ],
- toolsRight: [
- ,
- ],
- }}
- executeQueryOptions={{
- defaultFields: [
- TIMESTAMP_FIELD,
- MEASUREMENTS_FIELD,
- INDICES_FIELD,
- SEARCH_TYPE_FIELD,
- NODE_ID_FIELD,
- TOTAL_SHARDS_FIELD,
- ],
- }}
- allowNeutralSort={false}
- itemId={(query) => `${query.id}-${query.timestamp}`}
- />
+ filters: [
+ {
+ type: 'field_value_selection',
+ field: GROUP_BY_FIELD,
+ name: TYPE,
+ multiSelect: true,
+ options: [
+ {
+ value: 'NONE',
+ name: 'query',
+ view: 'query',
+ },
+ {
+ value: 'SIMILARITY',
+ name: 'group',
+ view: 'group',
+ },
+ ],
+ noOptionsMessage: 'No data available for the selected type', // Fallback message when no queries match
+ },
+ {
+ type: 'field_value_selection',
+ field: INDICES_FIELD,
+ name: INDICES,
+ multiSelect: true,
+ options: filterDuplicates(
+ queries.map((query) => {
+ const values = Array.from(new Set(query[INDICES_FIELD].flat()));
+ return {
+ value: values.join(','),
+ name: values.join(','),
+ view: values.join(', '),
+ };
+ })
+ ),
+ },
+ {
+ type: 'field_value_selection',
+ field: SEARCH_TYPE_FIELD,
+ name: SEARCH_TYPE,
+ multiSelect: false,
+ options: filterDuplicates(
+ queries.map((query) => ({
+ value: query[SEARCH_TYPE_FIELD],
+ name: query[SEARCH_TYPE_FIELD],
+ view: query[SEARCH_TYPE_FIELD],
+ }))
+ ),
+ },
+ {
+ type: 'field_value_selection',
+ field: NODE_ID_FIELD,
+ name: NODE_ID,
+ multiSelect: true,
+ options: filterDuplicates(
+ queries.map((query) => ({
+ value: query[NODE_ID_FIELD],
+ name: query[NODE_ID_FIELD],
+ view: query[NODE_ID_FIELD].replaceAll('_', ' '),
+ }))
+ ),
+ },
+ ],
+ toolsRight: [
+ ,
+ ],
+ }}
+ executeQueryOptions={{
+ defaultFields: [
+ TIMESTAMP_FIELD,
+ MEASUREMENTS_FIELD,
+ INDICES_FIELD,
+ SEARCH_TYPE_FIELD,
+ NODE_ID_FIELD,
+ TOTAL_SHARDS_FIELD,
+ ],
+ }}
+ allowNeutralSort={false}
+ itemId={(query) => `${query.id}-${query.timestamp}`}
+ />
+ >
);
};
diff --git a/public/pages/TopNQueries/TopNQueries.test.tsx b/public/pages/TopNQueries/TopNQueries.test.tsx
index cbbb238..cefe6bc 100644
--- a/public/pages/TopNQueries/TopNQueries.test.tsx
+++ b/public/pages/TopNQueries/TopNQueries.test.tsx
@@ -57,7 +57,7 @@ const setUpDefaultEnabledSettings = () => {
const renderTopNQueries = (type: string) =>
render(
-
+
);
@@ -179,7 +179,13 @@ describe('TopNQueries Component', () => {
// Render with initial time range
const { rerender } = render(
-
+
);
// Mock a new response for the time range update
@@ -190,7 +196,13 @@ describe('TopNQueries Component', () => {
// Re-render with updated time range to simulate a change
rerender(
-
+
);
// Verify that the component re-fetches data for the new time range
diff --git a/public/pages/TopNQueries/TopNQueries.tsx b/public/pages/TopNQueries/TopNQueries.tsx
index e112eb8..c4816e5 100644
--- a/public/pages/TopNQueries/TopNQueries.tsx
+++ b/public/pages/TopNQueries/TopNQueries.tsx
@@ -3,10 +3,11 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useCallback, useEffect, useState } from 'react';
+import React, { createContext, useCallback, useEffect, useState } from 'react';
import { Redirect, Route, Switch, useHistory, useLocation } from 'react-router-dom';
import { EuiTab, EuiTabs, EuiTitle, EuiSpacer } from '@elastic/eui';
-import { CoreStart } from 'opensearch-dashboards/public';
+import { AppMountParameters, CoreStart } from 'opensearch-dashboards/public';
+import { DataSourceManagementPluginSetup } from 'src/plugins/data_source_management/public';
import QueryInsights from '../QueryInsights/QueryInsights';
import Configuration from '../Configuration/Configuration';
import QueryDetails from '../QueryDetails/QueryDetails';
@@ -24,6 +25,8 @@ import {
import { MetricSettingsResponse } from '../../types';
import { getTimeAndUnitFromString } from '../Utils/MetricUtils';
import { parseDateString } from '../Utils/DateUtils';
+import { getDataSourceFromUrl } from '../../components/DataSourcePicker';
+import { DataSourceOption } from 'src/plugins/data_source_management/public/components/data_source_menu/types';
export const QUERY_INSIGHTS = '/queryInsights';
export const CONFIGURATION = '/configuration';
@@ -39,14 +42,27 @@ export interface GroupBySettings {
groupBy: string;
}
+export interface DataSourceContextType {
+ dataSource: DataSourceOption;
+ setDataSource: React.Dispatch>;
+}
+
+// export const LocalCluster = { label: 'Local cluster', id: '' };
+
+export const DataSourceContext = createContext(null);
+
const TopNQueries = ({
core,
depsStart,
+ params,
+ dataSourceManagement,
initialStart = 'now-1d',
initialEnd = 'now',
}: {
core: CoreStart;
depsStart: QueryInsightsDashboardsPluginStartDependencies;
+ params: AppMountParameters;
+ dataSourceManagement?: DataSourceManagementPluginSetup;
initialStart?: string;
initialEnd?: string;
}) => {
@@ -130,10 +146,11 @@ const TopNQueries = ({
const retrieveQueries = useCallback(
async (start: string, end: string) => {
const nullResponse = { response: { top_queries: [] } };
- const params = {
+ const apiParams = {
query: {
from: parseDateString(start),
to: parseDateString(end),
+ dataSourceId: getDataSourceFromUrl().id, // TODO: get this dynamically from the URL
},
};
const fetchMetric = async (endpoint: string) => {
@@ -141,7 +158,7 @@ const TopNQueries = ({
// TODO: #13 refactor the interface definitions for requests and responses
const response: { response: { top_queries: SearchQueryRecord[] } } = await core.http.get(
endpoint,
- params
+ apiParams
);
return {
response: {
@@ -215,7 +232,7 @@ const TopNQueries = ({
return transient ?? persistent;
};
- const resp = await core.http.get('/api/settings');
+ const resp = await core.http.get('/api/settings', {query: {dataSourceId: '738ffbd0-d8de-11ef-9d96-eff1abd421b8'}});
const persistentSettings = resp?.response?.persistent?.search?.insights?.top_queries;
const transientSettings = resp?.response?.transient?.search?.insights?.top_queries;
const metrics = [
@@ -280,6 +297,7 @@ const TopNQueries = ({
top_n_size: newTopN,
window_size: `${newWindowSize}${newTimeUnit === 'MINUTES' ? 'm' : 'h'}`,
group_by: newGroupBy,
+ dataSourceId: getDataSourceFromUrl().id, // TODO: get this dynamically from the URL
},
});
} catch (error) {
@@ -311,72 +329,87 @@ const TopNQueries = ({
retrieveQueries(currStart, currEnd);
}, [latencySettings, cpuSettings, memorySettings, currStart, currEnd, retrieveQueries]);
+ const dataSourceFromUrl = getDataSourceFromUrl();
+
+ const [dataSource, setDataSource] = useState(dataSourceFromUrl);
+
return (
-
-
-
- {() => {
- return ;
- }}
-
-
- {() => {
- return ;
- }}
-
-
-
-
- Query insights - Top N queries
-
-
- >
- }
- />
- {tabs.map(renderTab)}
-
-
-
-
-
-
- Query insights - Configuration
-
-
- >
- }
- />
+
+
+
+
+ {() => {
+ return ;
+ }}
+
+
+ {() => {
+ return ;
+ }}
+
+
+
+
+ Query insights - Top N queries
+
+
+ >
+ }
+ />
+ {tabs.map(renderTab)}
+
+
+
+
+
+
+ Query insights - Configuration
+
+
+ >
+ }
+ />
- {tabs.map(renderTab)}
-
-
-
-
-
-
+ {tabs.map(renderTab)}
+
+
+
+
+
+
+
);
};
diff --git a/public/pages/Utils/QueryUtils.ts b/public/pages/Utils/QueryUtils.ts
index 227a012..c615920 100644
--- a/public/pages/Utils/QueryUtils.ts
+++ b/public/pages/Utils/QueryUtils.ts
@@ -8,6 +8,7 @@ import { SearchQueryRecord } from '../../../types/types';
// Utility function to fetch query by id and time range
export const retrieveQueryById = async (
core: { http: { get: (endpoint: string, params: any) => Promise } },
+ dataSourceId: string,
start: string | null,
end: string | null,
id: string | null
@@ -15,6 +16,7 @@ export const retrieveQueryById = async (
const nullResponse = { response: { top_queries: [] } };
const params = {
query: {
+ dataSourceId,
from: start,
to: end,
id,
diff --git a/public/plugin.ts b/public/plugin.ts
index f1f42e1..3ddf161 100644
--- a/public/plugin.ts
+++ b/public/plugin.ts
@@ -12,6 +12,7 @@ import {
} from '../../../src/core/public';
import {
QueryInsightsDashboardsPluginSetup,
+ QueryInsightsDashboardsPluginSetupDependencies,
QueryInsightsDashboardsPluginStart,
QueryInsightsDashboardsPluginStartDependencies,
} from './types';
@@ -25,7 +26,10 @@ export class QueryInsightsDashboardsPlugin
{},
QueryInsightsDashboardsPluginStartDependencies
> {
- public setup(core: CoreSetup): QueryInsightsDashboardsPluginSetup {
+ public setup(
+ core: CoreSetup,
+ deps: QueryInsightsDashboardsPluginSetupDependencies
+ ): QueryInsightsDashboardsPluginSetup {
// Register an application into the side navigation menu
core.application.register({
id: PLUGIN_NAME,
@@ -48,7 +52,8 @@ export class QueryInsightsDashboardsPlugin
return renderApp(
coreStart,
depsStart as QueryInsightsDashboardsPluginStartDependencies,
- params
+ params,
+ deps.dataSourceManagement
);
},
});
diff --git a/public/types.ts b/public/types.ts
index 81ef52d..82ac0c7 100644
--- a/public/types.ts
+++ b/public/types.ts
@@ -3,12 +3,19 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { DataSourcePluginStart } from '../../../src/plugins/data_source/public';
+import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public';
import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public';
/* eslint-disable @typescript-eslint/no-empty-interface */
export interface QueryInsightsDashboardsPluginSetup {}
export interface QueryInsightsDashboardsPluginStart {}
-export interface QueryInsightsDashboardsPluginStartDependencies {}
+export interface QueryInsightsDashboardsPluginStartDependencies {
+ dataSource?: DataSourcePluginStart
+}
+export interface QueryInsightsDashboardsPluginSetupDependencies {
+ dataSourceManagement?: DataSourceManagementPluginSetup;
+}
/* eslint-enable @typescript-eslint/no-empty-interface */
export interface MetricSettingsResponse {
@@ -19,4 +26,5 @@ export interface MetricSettingsResponse {
export interface AppPluginStartDependencies {
navigation: NavigationPublicPluginStart;
+ dataSource?: DataSourcePluginStart;
}
diff --git a/server/plugin.ts b/server/plugin.ts
index fa7755f..0ed54d2 100644
--- a/server/plugin.ts
+++ b/server/plugin.ts
@@ -15,6 +15,11 @@ import { QueryInsightsPlugin } from './clusters/queryInsightsPlugin';
import { QueryInsightsDashboardsPluginSetup, QueryInsightsDashboardsPluginStart } from './types';
import { defineRoutes } from './routes';
+import { DataSourcePluginSetup } from '../../../src/plugins/data_source/server/types';
+
+export interface QueryInsightsDashboardsPluginSetupDependencies {
+ dataSource: DataSourcePluginSetup;
+}
export class QueryInsightsDashboardsPlugin
implements Plugin {
@@ -24,8 +29,8 @@ export class QueryInsightsDashboardsPlugin
this.logger = initializerContext.logger.get();
}
- public setup(core: CoreSetup) {
- this.logger.debug('query-insights-dashboards: Setup');
+ public setup(core: CoreSetup, { dataSource }: QueryInsightsDashboardsPluginSetupDependencies) {
+ const dataSourceEnabled = !!dataSource;
const router = core.http.createRouter();
const queryInsightsClient: ILegacyCustomClusterClient = core.opensearch.legacy.createClient(
'opensearch_queryInsights',
@@ -33,6 +38,10 @@ export class QueryInsightsDashboardsPlugin
plugins: [QueryInsightsPlugin],
}
);
+ if (dataSourceEnabled) {
+ dataSource.registerCustomApiSchema(QueryInsightsPlugin);
+ }
+
// @ts-ignore
core.http.registerRouteHandlerContext('queryInsights_plugin', (_context, _request) => {
return {
@@ -42,7 +51,7 @@ export class QueryInsightsDashboardsPlugin
});
// Register server side APIs
- defineRoutes(router);
+ defineRoutes(router, dataSourceEnabled);
return {};
}
diff --git a/server/routes/index.ts b/server/routes/index.ts
index 5dcf314..ea6c4a4 100644
--- a/server/routes/index.ts
+++ b/server/routes/index.ts
@@ -5,24 +5,42 @@
import { schema } from '@osd/config-schema';
import { IRouter } from '../../../../src/core/server';
-export function defineRoutes(router: IRouter) {
+export function defineRoutes(router: IRouter, dataSoureEnabled: boolean) {
router.get(
{
path: '/api/top_queries',
- validate: false,
+ validate: {
+ query: schema.object({
+ dataSourceId: schema.maybe(schema.string()),
+ }),
+ },
},
async (context, request, response) => {
try {
- const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request)
- .callAsCurrentUser;
- const res = await client('queryInsights.getTopNQueries');
- return response.custom({
- statusCode: 200,
- body: {
- ok: true,
- response: res,
- },
- });
+ if (dataSoureEnabled) {
+ const client = context.dataSource.opensearch.legacy.getClient(
+ request.query?.dataSourceId
+ );
+ const res = await client.callAPI('queryInsights.getTopNQueries', {});
+ return response.custom({
+ statusCode: 200,
+ body: {
+ ok: true,
+ response: res,
+ },
+ });
+ } else {
+ const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request)
+ .callAsCurrentUser;
+ const res = await client('queryInsights.getTopNQueries');
+ return response.custom({
+ statusCode: 200,
+ body: {
+ ok: true,
+ response: res,
+ },
+ });
+ }
} catch (error) {
console.error('Unable to get top queries: ', error);
return response.ok({
@@ -43,6 +61,7 @@ export function defineRoutes(router: IRouter) {
from: schema.maybe(schema.string({ defaultValue: '' })),
to: schema.maybe(schema.string({ defaultValue: '' })),
id: schema.maybe(schema.string({ defaultValue: '' })),
+ dataSourceId: schema.maybe(schema.string()),
}),
},
},
@@ -50,20 +69,36 @@ export function defineRoutes(router: IRouter) {
try {
const { from, to, id } = request.query;
const params = { from, to, id };
- const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request)
- .callAsCurrentUser;
-
- const res =
- id != null
- ? await client('queryInsights.getTopNQueriesLatencyForId', params)
- : await client('queryInsights.getTopNQueriesLatency', params);
- return response.custom({
- statusCode: 200,
- body: {
- ok: true,
- response: res,
- },
- });
+ if (dataSoureEnabled) {
+ const client = context.dataSource.opensearch.legacy.getClient(
+ request.query?.dataSourceId
+ );
+ const res =
+ id != null
+ ? await client.callAPI('queryInsights.getTopNQueriesLatencyForId', params)
+ : await client.callAPI('queryInsights.getTopNQueriesLatency', params);
+ return response.custom({
+ statusCode: 200,
+ body: {
+ ok: true,
+ response: res,
+ },
+ });
+ } else {
+ const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request)
+ .callAsCurrentUser;
+ const res =
+ id != null
+ ? await client('queryInsights.getTopNQueriesLatencyForId', params)
+ : await client('queryInsights.getTopNQueriesLatency', params);
+ return response.custom({
+ statusCode: 200,
+ body: {
+ ok: true,
+ response: res,
+ },
+ });
+ }
} catch (error) {
console.error('Unable to get top queries (latency): ', error);
return response.ok({
@@ -84,6 +119,7 @@ export function defineRoutes(router: IRouter) {
from: schema.maybe(schema.string({ defaultValue: '' })),
to: schema.maybe(schema.string({ defaultValue: '' })),
id: schema.maybe(schema.string({ defaultValue: '' })),
+ dataSourceId: schema.maybe(schema.string()),
}),
},
},
@@ -91,20 +127,39 @@ export function defineRoutes(router: IRouter) {
try {
const { from, to, id } = request.query;
const params = { from, to, id };
- const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request)
- .callAsCurrentUser;
-
- const res =
- id != null
- ? await client('queryInsights.getTopNQueriesCpuForId', params)
- : await client('queryInsights.getTopNQueriesCpu', params);
- return response.custom({
- statusCode: 200,
- body: {
- ok: true,
- response: res,
- },
- });
+ if (dataSoureEnabled) {
+
+ const client = context.dataSource.opensearch.legacy.getClient(
+ request.query?.dataSourceId
+ );
+ const res =
+ id != null
+ ? await client.callAPI('queryInsights.getTopNQueriesCpuForId', params)
+ : await client.callAPI('queryInsights.getTopNQueriesCpu', params);
+ return response.custom({
+ statusCode: 200,
+ body: {
+ ok: true,
+ response: res,
+ },
+ });
+ } else {
+ console.log(request.query.dataSourceId)
+ console.log(params);
+ const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request)
+ .callAsCurrentUser;
+ const res =
+ id != null
+ ? await client('queryInsights.getTopNQueriesCpuForId', params)
+ : await client('queryInsights.getTopNQueriesCpu', params);
+ return response.custom({
+ statusCode: 200,
+ body: {
+ ok: true,
+ response: res,
+ },
+ });
+ }
} catch (error) {
console.error('Unable to get top queries (cpu): ', error);
return response.ok({
@@ -125,6 +180,7 @@ export function defineRoutes(router: IRouter) {
from: schema.maybe(schema.string({ defaultValue: '' })),
to: schema.maybe(schema.string({ defaultValue: '' })),
id: schema.maybe(schema.string({ defaultValue: '' })),
+ dataSourceId: schema.maybe(schema.string()),
}),
},
},
@@ -132,20 +188,36 @@ export function defineRoutes(router: IRouter) {
try {
const { from, to, id } = request.query;
const params = { from, to, id };
- const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request)
- .callAsCurrentUser;
-
- const res =
- id != null
- ? await client('queryInsights.getTopNQueriesMemoryForId', params)
- : await client('queryInsights.getTopNQueriesMemory', params);
- return response.custom({
- statusCode: 200,
- body: {
- ok: true,
- response: res,
- },
- });
+ if (dataSoureEnabled) {
+ const client = context.dataSource.opensearch.legacy.getClient(
+ request.query?.dataSourceId
+ );
+ const res =
+ id != null
+ ? await client.callAPI('queryInsights.getTopNQueriesMemoryForId', params)
+ : await client.callAPI('queryInsights.getTopNQueriesMemory', params);
+ return response.custom({
+ statusCode: 200,
+ body: {
+ ok: true,
+ response: res,
+ },
+ });
+ } else {
+ const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request)
+ .callAsCurrentUser;
+ const res =
+ id != null
+ ? await client('queryInsights.getTopNQueriesMemoryForId', params)
+ : await client('queryInsights.getTopNQueriesMemory', params);
+ return response.custom({
+ statusCode: 200,
+ body: {
+ ok: true,
+ response: res,
+ },
+ });
+ }
} catch (error) {
console.error('Unable to get top queries (memory): ', error);
return response.ok({
@@ -161,20 +233,38 @@ export function defineRoutes(router: IRouter) {
router.get(
{
path: '/api/settings',
- validate: false,
+ validate: {
+ query: schema.object({
+ dataSourceId: schema.maybe(schema.string()),
+ }),
+ },
},
async (context, request, response) => {
try {
- const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request)
- .callAsCurrentUser;
- const res = await client('queryInsights.getSettings');
- return response.custom({
- statusCode: 200,
- body: {
- ok: true,
- response: res,
- },
- });
+ if (dataSoureEnabled) {
+ const client = context.dataSource.opensearch.legacy.getClient(
+ request.query?.dataSourceId
+ );
+ const res = await client.callAPI('queryInsights.getSettings', {});
+ return response.custom({
+ statusCode: 200,
+ body: {
+ ok: true,
+ response: res,
+ },
+ });
+ } else {
+ const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request)
+ .callAsCurrentUser;
+ const res = await client('queryInsights.getSettings');
+ return response.custom({
+ statusCode: 200,
+ body: {
+ ok: true,
+ response: res,
+ },
+ });
+ }
} catch (error) {
console.error('Unable to get top queries: ', error);
return response.ok({
@@ -197,14 +287,13 @@ export function defineRoutes(router: IRouter) {
top_n_size: schema.maybe(schema.string({ defaultValue: '' })),
window_size: schema.maybe(schema.string({ defaultValue: '' })),
group_by: schema.maybe(schema.string({ defaultValue: '' })),
+ dataSourceId: schema.maybe(schema.string()),
}),
},
},
async (context, request, response) => {
try {
const query = request.query;
- const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request)
- .callAsCurrentUser;
const params = {
body: {
persistent: {
@@ -215,14 +304,30 @@ export function defineRoutes(router: IRouter) {
},
},
};
- const res = await client('queryInsights.setSettings', params);
- return response.custom({
- statusCode: 200,
- body: {
- ok: true,
- response: res,
- },
- });
+ if (dataSoureEnabled) {
+ const client = context.dataSource.opensearch.legacy.getClient(
+ request.query?.dataSourceId
+ );
+ const res = await client.callAPI('queryInsights.setSettings', params);
+ return response.custom({
+ statusCode: 200,
+ body: {
+ ok: true,
+ response: res,
+ },
+ });
+ } else {
+ const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request)
+ .callAsCurrentUser;
+ const res = await client('queryInsights.setSettings', params);
+ return response.custom({
+ statusCode: 200,
+ body: {
+ ok: true,
+ response: res,
+ },
+ });
+ }
} catch (error) {
console.error('Unable to set settings: ', error);
return response.ok({