diff --git a/cypress/e2e/3_configurations.cy.js b/cypress/e2e/3_configurations.cy.js
index fe705b8..ac8fc58 100644
--- a/cypress/e2e/3_configurations.cy.js
+++ b/cypress/e2e/3_configurations.cy.js
@@ -11,6 +11,13 @@ const clearAll = () => {
cy.disableTopQueries(METRICS.MEMORY);
};
+const toggleMetricEnabled = async () => {
+ cy.get('button[data-test-subj="top-n-metric-toggle"]').trigger('mouseover');
+ cy.wait(1000);
+ cy.get('button[data-test-subj="top-n-metric-toggle"]').click({ force: true });
+ cy.wait(1000);
+};
+
describe('Query Insights Configurations Page', () => {
beforeEach(() => {
clearAll();
@@ -66,22 +73,30 @@ describe('Query Insights Configurations Page', () => {
*/
it('should allow enabling and disabling metrics', () => {
// Validate the switch for enabling/disabling metrics
- cy.get('button[role="switch"]').should('exist');
- // Toggle the switch
- cy.get('button[role="switch"]')
- .first()
- .should('have.attr', 'aria-checked', 'false') // Initially disabled
- .click()
- .should('have.attr', 'aria-checked', 'true'); // After toggling, it should be enabled
+ cy.get('button[data-test-subj="top-n-metric-toggle"]')
+ .should('exist')
+ .and('have.attr', 'aria-checked', 'false') // Initially disabled)
+ .trigger('mouseover')
+ .click();
+ cy.wait(1000);
+
+ cy.get('button[data-test-subj="top-n-metric-toggle"]').should(
+ 'have.attr',
+ 'aria-checked',
+ 'true'
+ ); // After toggling, it should be enabled
// Re-enable the switch
- cy.get('button[role="switch"]').first().click().should('have.attr', 'aria-checked', 'false');
+ cy.get('button[data-test-subj="top-n-metric-toggle"]')
+ .trigger('mouseover')
+ .click()
+ .should('have.attr', 'aria-checked', 'false');
});
/**
* Validate the value of N (count) input
*/
it('should allow updating the value of N (count)', () => {
- cy.get('button[role="switch"]').first().click();
+ toggleMetricEnabled();
// Locate the input for N
cy.get('input[type="number"]').should('have.attr', 'value', '3'); // Default 3
// Change the value to 50
@@ -95,7 +110,7 @@ describe('Query Insights Configurations Page', () => {
* Validate the window size dropdowns
*/
it('should allow selecting a window size and unit', () => {
- cy.get('button[role="switch"]').first().click();
+ toggleMetricEnabled();
// Validate default values
cy.get('select#timeUnit').should('have.value', 'MINUTES'); // Default unit is "Minute(s)"
// Test valid time unit selection
@@ -192,7 +207,7 @@ describe('Query Insights Configurations Page', () => {
* After saving the status panel should show the correct status
*/
it('should allow saving the configuration', () => {
- cy.get('button[role="switch"]').first().click();
+ toggleMetricEnabled();
cy.get('select#timeUnit').select('MINUTES');
cy.get('select#minutes').select('5');
cy.get('button[data-test-subj="save-config-button"]').click();
diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json
index 7310231..4dd344a 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..3621deb
--- /dev/null
+++ b/public/components/DataSourcePicker.tsx
@@ -0,0 +1,88 @@
+/*
+ * 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;
+ onManageDataSource: () => void;
+ onSelectedDataSource: () => void;
+ dataSourcePickerReadOnly: boolean;
+}
+
+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,
+ onManageDataSource,
+ onSelectedDataSource,
+ dataSourcePickerReadOnly,
+ } = 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]);
+ onSelectedDataSource();
+ };
+
+ return dataSourceEnabled ? (
+
+ ) : null;
+ },
+ (prevProps, newProps) =>
+ prevProps.selectedDataSource.id === newProps.selectedDataSource.id &&
+ prevProps.dataSourcePickerReadOnly === newProps.dataSourcePickerReadOnly
+);
diff --git a/public/components/app.test.tsx b/public/components/app.test.tsx
index eae7f7e..0ac8c47 100644
--- a/public/components/app.test.tsx
+++ b/public/components/app.test.tsx
@@ -8,28 +8,41 @@ import { render } from '@testing-library/react';
import { coreMock } from '../../../../src/core/public/mocks';
import { MemoryRouter as Router } from 'react-router-dom';
import { QueryInsightsDashboardsApp } from './app';
+import { createMemoryHistory } from 'history';
describe(' spec', () => {
it('renders the component', () => {
- const mockHttpStart = {
- basePath: {
- serverBasePath: '/app/opensearch-dashboards',
+ const coreStart = coreMock.createStart();
+ // Mock AppMountParameters
+ const params = {
+ appBasePath: '/',
+ history: createMemoryHistory(),
+ setHeaderActionMenu: jest.fn(),
+ element: document.createElement('div'),
+ };
+ // Mock plugin dependencies
+ const depsStart = {
+ navigation: {
+ ui: { TopNavMenu: () => null },
+ },
+ data: {
+ dataSources: {
+ dataSourceService: jest.fn(),
+ },
},
};
- const coreStart = coreMock.createStart();
-
const { container } = render(
null },
- } as any
- }
- notifications={{} as any}
+ depsStart={depsStart}
+ params={params}
+ dataSourceManagement={{
+ ui: {
+ getDataSourceMenu: jest.fn(),
+ getDataSourceSelector: jest.fn(),
+ },
+ }}
/>
);
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..91680f1 100644
--- a/public/pages/Configuration/Configuration.test.tsx
+++ b/public/pages/Configuration/Configuration.test.tsx
@@ -8,6 +8,7 @@ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { MemoryRouter } from 'react-router-dom';
import Configuration from './Configuration';
+import { DataSourceContext } from '../TopNQueries/TopNQueries';
const mockConfigInfo = jest.fn();
const mockCoreStart = {
@@ -39,17 +40,34 @@ const groupBySettings = {
groupBy: 'SIMILARITY',
};
+const dataSourceMenuMock = jest.fn(() => Mock DataSourceMenu
);
+
+const dataSourceManagementMock = {
+ ui: {
+ getDataSourceMenu: jest.fn().mockReturnValue(dataSourceMenuMock),
+ },
+};
+const mockDataSourceContext = {
+ dataSource: { id: 'test', label: 'Test' },
+ setDataSource: jest.fn(),
+};
+
const renderConfiguration = (overrides = {}) =>
render(
-
+
+
+
);
diff --git a/public/pages/Configuration/Configuration.tsx b/public/pages/Configuration/Configuration.tsx
index 47e099a..709a004 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,22 @@ 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 +47,9 @@ const Configuration = ({
groupBySettings,
configInfo,
core,
+ depsStart,
+ params,
+ dataSourceManagement,
}: {
latencySettings: MetricSettings;
cpuSettings: MetricSettings;
@@ -46,6 +57,9 @@ const Configuration = ({
groupBySettings: GroupBySettings;
configInfo: any;
core: CoreStart;
+ params: AppMountParameters;
+ dataSourceManagement?: DataSourceManagementPluginSetup;
+ depsStart: QueryInsightsDashboardsPluginStartDependencies;
}) => {
const history = useHistory();
const location = useLocation();
@@ -56,6 +70,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 +185,19 @@ const Configuration = ({
return (
+
{}}
+ onSelectedDataSource={() => {
+ configInfo(true);
+ }}
+ dataSourcePickerReadOnly={false}
+ />
@@ -214,7 +242,12 @@ const Configuration = ({
-
+
diff --git a/public/pages/Configuration/__snapshots__/Configuration.test.tsx.snap b/public/pages/Configuration/__snapshots__/Configuration.test.tsx.snap
index 091a526..23ad8c4 100644
--- a/public/pages/Configuration/__snapshots__/Configuration.test.tsx.snap
+++ b/public/pages/Configuration/__snapshots__/Configuration.test.tsx.snap
@@ -157,6 +157,7 @@ exports[`Configuration Component renders with default settings: should match def
aria-checked="true"
aria-labelledby="random_html_id"
class="euiSwitch__button"
+ data-test-subj="top-n-metric-toggle"
id="random_html_id"
role="switch"
type="button"
@@ -970,6 +971,7 @@ exports[`Configuration Component updates state when toggling metrics and enables
aria-checked="true"
aria-labelledby="random_html_id"
class="euiSwitch__button"
+ data-test-subj="top-n-metric-toggle"
id="random_html_id"
role="switch"
type="button"
diff --git a/public/pages/QueryDetails/QueryDetails.test.tsx b/public/pages/QueryDetails/QueryDetails.test.tsx
index a545fca..3b51c6f 100644
--- a/public/pages/QueryDetails/QueryDetails.test.tsx
+++ b/public/pages/QueryDetails/QueryDetails.test.tsx
@@ -13,6 +13,7 @@ import plotly from 'plotly.js-dist';
import { MemoryRouter, Route } from 'react-router-dom';
import hash from 'object-hash';
import { retrieveQueryById } from '../Utils/QueryUtils';
+import { DataSourceContext } from '../TopNQueries/TopNQueries';
jest.mock('plotly.js-dist', () => ({
newPlot: jest.fn(),
@@ -31,6 +32,18 @@ const mockCoreStart = {
},
};
+const dataSourceMenuMock = jest.fn(() => Mock DataSourceMenu
);
+
+const dataSourceManagementMock = {
+ ui: {
+ getDataSourceMenu: jest.fn().mockReturnValue(dataSourceMenuMock),
+ },
+};
+const mockDataSourceContext = {
+ dataSource: { id: 'test', label: 'Test' },
+ setDataSource: jest.fn(),
+};
+
const mockQuery = MockQueries()[0];
describe('QueryDetails component', () => {
@@ -48,9 +61,16 @@ describe('QueryDetails component', () => {
)}&from=2025-01-21T22:30:33.347Z&to=2025-01-22T22:30:33.347Z`,
]}
>
-
-
-
+
+
+
+
+
);
};
diff --git a/public/pages/QueryDetails/QueryDetails.tsx b/public/pages/QueryDetails/QueryDetails.tsx
index 4ecb2d5..ee28bc1 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,28 @@ 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 +52,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) => {
@@ -51,12 +61,12 @@ const QueryDetails = ({
return `${month} ${day}, ${year} @ ${date.toLocaleTimeString('en-US')}`;
}, []);
- useEffect(() => {
- const fetchQueryDetails = async () => {
- const retrievedQuery = await retrieveQueryById(core, from, to, id);
- setQuery(retrievedQuery);
- };
+ const fetchQueryDetails = async () => {
+ const retrievedQuery = await retrieveQueryById(core, getDataSourceFromUrl().id, from, to, id);
+ setQuery(retrievedQuery);
+ };
+ useEffect(() => {
if (id && from && to) {
fetchQueryDetails();
}
@@ -134,7 +144,17 @@ const QueryDetails = ({
>
}
/>
-
+ {}}
+ onSelectedDataSource={fetchQueryDetails}
+ dataSourcePickerReadOnly={true}
+ />
diff --git a/public/pages/QueryGroupDetails/QueryGroupDetails.test.tsx b/public/pages/QueryGroupDetails/QueryGroupDetails.test.tsx
index db49f37..9965f39 100644
--- a/public/pages/QueryGroupDetails/QueryGroupDetails.test.tsx
+++ b/public/pages/QueryGroupDetails/QueryGroupDetails.test.tsx
@@ -11,6 +11,7 @@ import React from 'react';
import { mockQueries } from '../../../test/mocks/mockQueries';
import '@testing-library/jest-dom/extend-expect';
import { retrieveQueryById } from '../Utils/QueryUtils';
+import { DataSourceContext } from '../TopNQueries/TopNQueries';
jest.mock('object-hash', () => jest.fn(() => '8c1e50c035663459d567fa11d8eb494d'));
@@ -29,6 +30,18 @@ jest.mock('react-ace', () => ({
default: () => Mocked Ace Editor
,
}));
+const dataSourceMenuMock = jest.fn(() => Mock DataSourceMenu
);
+
+const dataSourceManagementMock = {
+ ui: {
+ getDataSourceMenu: jest.fn().mockReturnValue(dataSourceMenuMock),
+ },
+};
+const mockDataSourceContext = {
+ dataSource: { id: 'test', label: 'Test' },
+ setDataSource: jest.fn(),
+};
+
const mockQuery = mockQueries[0];
describe('QueryGroupDetails', () => {
@@ -51,9 +64,16 @@ describe('QueryGroupDetails', () => {
-
-
-
+
+
+
+
+
);
};
@@ -78,6 +98,7 @@ describe('QueryGroupDetails', () => {
await waitFor(() => {
expect(retrieveQueryById).toHaveBeenCalledWith(
coreMock,
+ undefined,
'1632441600000',
'1632528000000',
'mockId'
diff --git a/public/pages/QueryGroupDetails/QueryGroupDetails.tsx b/public/pages/QueryGroupDetails/QueryGroupDetails.tsx
index f679815..6ec318b 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,20 @@ import { QueryInsightsDashboardsPluginStartDependencies } from '../../types';
import { PageHeader } from '../../components/PageHeader';
import { SearchQueryRecord } from '../../../types/types';
import { retrieveQueryById } from '../Utils/QueryUtils';
+import {
+ getDataSourceFromUrl,
+ QueryInsightsDataSourceMenu,
+} from '../../components/DataSourcePicker';
export const QueryGroupDetails = ({
core,
depsStart,
+ params,
+ dataSourceManagement,
}: {
core: CoreStart;
+ params: AppMountParameters;
+ dataSourceManagement?: DataSourceManagementPluginSetup;
depsStart: QueryInsightsDashboardsPluginStartDependencies;
}) => {
// Get url parameters
@@ -45,6 +54,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);
@@ -54,12 +64,12 @@ export const QueryGroupDetails = ({
const history = useHistory();
- useEffect(() => {
- const fetchQueryDetails = async () => {
- const retrievedQuery = await retrieveQueryById(core, from, to, id);
- setQuery(retrievedQuery);
- };
+ const fetchQueryDetails = async () => {
+ const retrievedQuery = await retrieveQueryById(core, getDataSourceFromUrl().id, from, to, id);
+ setQuery(retrievedQuery);
+ };
+ useEffect(() => {
if (id && from && to) {
fetchQueryDetails();
}
@@ -149,6 +159,17 @@ export const QueryGroupDetails = ({
>
}
/>
+ {}}
+ onSelectedDataSource={fetchQueryDetails}
+ dataSourcePickerReadOnly={true}
+ />
diff --git a/public/pages/QueryInsights/QueryInsights.test.tsx b/public/pages/QueryInsights/QueryInsights.test.tsx
index d6708e3..8552a77 100644
--- a/public/pages/QueryInsights/QueryInsights.test.tsx
+++ b/public/pages/QueryInsights/QueryInsights.test.tsx
@@ -9,6 +9,7 @@ import '@testing-library/jest-dom/extend-expect';
import QueryInsights from './QueryInsights';
import { MemoryRouter } from 'react-router-dom';
import { MockQueries } from '../../../test/testUtils';
+import { DataSourceContext } from '../TopNQueries/TopNQueries';
// Mock functions and data
const mockOnTimeChange = jest.fn();
@@ -18,21 +19,38 @@ const mockCore = {
},
};
+const dataSourceMenuMock = jest.fn(() => Mock DataSourceMenu
);
+
+const dataSourceManagementMock = {
+ ui: {
+ getDataSourceMenu: jest.fn().mockReturnValue(dataSourceMenuMock),
+ },
+};
+const mockDataSourceContext = {
+ dataSource: { id: 'test', label: 'Test' },
+ setDataSource: jest.fn(),
+};
+
const sampleQueries = MockQueries();
const renderQueryInsights = () =>
render(
-
+
+
+
);
diff --git a/public/pages/QueryInsights/QueryInsights.tsx b/public/pages/QueryInsights/QueryInsights.tsx
index c4ec1ae..69a5591 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,10 @@ const QueryInsights = ({
currStart,
currEnd,
core,
+ depsStart,
+ params,
+ retrieveQueries,
+ dataSourceManagement,
}: {
queries: SearchQueryRecord[];
loading: boolean;
@@ -50,6 +57,10 @@ const QueryInsights = ({
currStart: string;
currEnd: string;
core: CoreStart;
+ params: AppMountParameters;
+ dataSourceManagement?: DataSourceManagementPluginSetup;
+ retrieveQueries?: any;
+ depsStart: QueryInsightsDashboardsPluginStartDependencies;
}) => {
const history = useHistory();
const location = useLocation();
@@ -57,6 +68,7 @@ const QueryInsights = ({
const from = parseDateString(currStart);
const to = parseDateString(currEnd);
+ const { dataSource, setDataSource } = useContext(DataSourceContext)!;
useEffect(() => {
core.chrome.setBreadcrumbs([
@@ -258,110 +270,125 @@ 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],
- }))
- ),
+ <>
+ {}}
+ onSelectedDataSource={() => {
+ retrieveQueries(currStart, currEnd);
+ }}
+ dataSourcePickerReadOnly={false}
+ />
+ ({
- 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..66937fc 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(
-
+
);
@@ -99,7 +99,9 @@ describe('TopNQueries Component', () => {
(mockCore.http.get as jest.Mock).mockResolvedValueOnce(mockSettingsResponse);
const container = renderTopNQueries(CONFIGURATION);
await waitFor(() => {
- expect(mockCore.http.get).toHaveBeenCalledWith('/api/settings');
+ expect(mockCore.http.get).toHaveBeenCalledWith('/api/settings', {
+ query: { dataSourceId: undefined },
+ });
expect(screen.getByText('Mocked Configuration')).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
@@ -110,7 +112,7 @@ describe('TopNQueries Component', () => {
const container = renderTopNQueries(QUERY_INSIGHTS);
await waitFor(() => {
// Verify each endpoint is called
- expect(mockCore.http.get).toHaveBeenCalledWith('/api/settings');
+ expect(mockCore.http.get).toHaveBeenCalledWith('/api/settings', expect.any(Object));
expect(mockCore.http.get).toHaveBeenCalledWith(
'/api/top_queries/latency',
expect.any(Object)
@@ -153,7 +155,7 @@ describe('TopNQueries Component', () => {
const container = renderTopNQueries(QUERY_INSIGHTS);
await waitFor(() => {
// Verify each endpoint is called
- expect(mockCore.http.get).toHaveBeenCalledWith('/api/settings');
+ expect(mockCore.http.get).toHaveBeenCalledWith('/api/settings', expect.any(Object));
expect(mockCore.http.get).toHaveBeenCalledWith(
'/api/top_queries/latency',
expect.any(Object)
@@ -179,7 +181,13 @@ describe('TopNQueries Component', () => {
// Render with initial time range
const { rerender } = render(
-
+
);
// Mock a new response for the time range update
@@ -190,14 +198,20 @@ 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
await waitFor(() => {
// 1 initial call for settings, 3 each for the initial rendering and re-rendering
expect(mockCore.http.get).toHaveBeenCalledTimes(7);
- expect(mockCore.http.get).toHaveBeenCalledWith('/api/settings');
+ expect(mockCore.http.get).toHaveBeenCalledWith('/api/settings', expect.any(Object));
expect(mockCore.http.get).toHaveBeenCalledWith(
'/api/top_queries/latency',
expect.any(Object)
diff --git a/public/pages/TopNQueries/TopNQueries.tsx b/public/pages/TopNQueries/TopNQueries.tsx
index e112eb8..9690e0a 100644
--- a/public/pages/TopNQueries/TopNQueries.tsx
+++ b/public/pages/TopNQueries/TopNQueries.tsx
@@ -3,10 +3,12 @@
* 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 { DataSourceOption } from 'src/plugins/data_source_management/public/components/data_source_menu/types';
import QueryInsights from '../QueryInsights/QueryInsights';
import Configuration from '../Configuration/Configuration';
import QueryDetails from '../QueryDetails/QueryDetails';
@@ -24,6 +26,7 @@ import {
import { MetricSettingsResponse } from '../../types';
import { getTimeAndUnitFromString } from '../Utils/MetricUtils';
import { parseDateString } from '../Utils/DateUtils';
+import { getDataSourceFromUrl } from '../../components/DataSourcePicker';
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;
}) => {
@@ -127,13 +143,15 @@ const TopNQueries = ({
);
+ // TODO: refactor retrieveQueries and retrieveConfigInfo into a Util function
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 +159,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 +233,10 @@ 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 resp = await core.http.get('/api/settings', {
+ query: { dataSourceId: getDataSourceFromUrl().id },
+ });
const persistentSettings = resp?.response?.persistent?.search?.insights?.top_queries;
const transientSettings = resp?.response?.transient?.search?.insights?.top_queries;
const metrics = [
@@ -252,6 +273,10 @@ const TopNQueries = ({
currWindowSize: time,
currTimeUnit: timeUnits,
});
+ } else {
+ setMetricSettings(metricType, {
+ isEnabled: false,
+ });
}
});
const groupBy = getMergedGroupBySettings(
@@ -280,6 +305,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 +337,99 @@ 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..d8374ae 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..3c2455d 100644
--- a/server/routes/index.ts
+++ b/server/routes/index.ts
@@ -5,24 +5,43 @@
import { schema } from '@osd/config-schema';
import { IRouter } from '../../../../src/core/server';
-export function defineRoutes(router: IRouter) {
+export function defineRoutes(router: IRouter, dataSourceEnabled: 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,
- },
- });
+ // data source disabled
+ if (!dataSourceEnabled || !request.query?.dataSourceId) {
+ 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,
+ },
+ });
+ } else {
+ 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,
+ },
+ });
+ }
} catch (error) {
console.error('Unable to get top queries: ', error);
return response.ok({
@@ -43,6 +62,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 +70,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 (!dataSourceEnabled || !request.query?.dataSourceId) {
+ 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,
+ },
+ });
+ } else {
+ 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,
+ },
+ });
+ }
} catch (error) {
console.error('Unable to get top queries (latency): ', error);
return response.ok({
@@ -84,6 +120,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 +128,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.getTopNQueriesCpuForId', params)
- : await client('queryInsights.getTopNQueriesCpu', params);
- return response.custom({
- statusCode: 200,
- body: {
- ok: true,
- response: res,
- },
- });
+ if (!dataSourceEnabled || !request.query?.dataSourceId) {
+ 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,
+ },
+ });
+ } else {
+ 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,
+ },
+ });
+ }
} catch (error) {
console.error('Unable to get top queries (cpu): ', error);
return response.ok({
@@ -125,6 +178,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 +186,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 (!dataSourceEnabled || !request.query?.dataSourceId) {
+ 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,
+ },
+ });
+ } else {
+ 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,
+ },
+ });
+ }
} catch (error) {
console.error('Unable to get top queries (memory): ', error);
return response.ok({
@@ -161,20 +231,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 (!dataSourceEnabled || !request.query?.dataSourceId) {
+ 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,
+ },
+ });
+ } else {
+ 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,
+ },
+ });
+ }
} catch (error) {
console.error('Unable to get top queries: ', error);
return response.ok({
@@ -197,14 +285,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 +302,30 @@ export function defineRoutes(router: IRouter) {
},
},
};
- const res = await client('queryInsights.setSettings', params);
- return response.custom({
- statusCode: 200,
- body: {
- ok: true,
- response: res,
- },
- });
+ if (!dataSourceEnabled || !request.query?.dataSourceId) {
+ 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,
+ },
+ });
+ } else {
+ 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,
+ },
+ });
+ }
} catch (error) {
console.error('Unable to set settings: ', error);
return response.ok({