From 1bed6ab6c9def2271fcde8981e25bfea35445c05 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Thu, 25 Apr 2024 17:56:29 -0700 Subject: [PATCH] Add MDS UTs and bug fixes (#734) * working on ut Signed-off-by: Jackie Han * update ut Signed-off-by: Jackie Han * update UTs and add bug fixes Signed-off-by: Jackie Han --------- Signed-off-by: Jackie Han --- package.json | 2 +- .../__tests__/AnomaliesChart.test.tsx | 19 +++++- .../__tests__/AnomalyDetailsChart.test.tsx | 56 +++++++++++----- .../__tests__/AnomalyOccurrenceChart.test.tsx | 57 +++++++++++----- .../containers/ConfigureModel.tsx | 67 +++++++++++++------ .../__tests__/ConfigureModel.test.tsx | 9 +++ .../__tests__/EmptyDashboard.test.tsx | 17 ++++- .../EmptyDashboard.test.tsx.snap | 4 +- .../__tests__/AnomaliesLiveCharts.test.tsx | 28 +++++++- .../Dashboard/Container/DashboardOverview.tsx | 23 ++++--- .../components/__tests__/DataFilter.test.tsx | 4 +- .../containers/DefineDetector.tsx | 57 ++++++++++------ .../__tests__/DefineDetector.test.tsx | 10 +++ .../containers/DetectorDetail.tsx | 24 ++++--- .../DetectorJobs/containers/DetectorJobs.tsx | 22 ++++-- .../__tests__/DetectorJobs.test.tsx | 8 +++ .../containers/AnomalyResults.tsx | 21 ++++-- .../__tests__/EmptyMessage.test.tsx | 33 +++++++-- .../__snapshots__/EmptyMessage.test.tsx.snap | 4 +- .../ConfirmDeleteDetectorsModal.test.tsx | 4 +- .../DetectorsList/containers/List/List.tsx | 24 ++++--- .../containers/List/__tests__/List.test.tsx | 12 +++- .../utils/__tests__/helpers.test.ts | 3 + .../utils/__tests__/tableUtils.test.tsx | 16 ++--- .../__tests__/SampleDataBox.test.tsx | 41 +++++++++++- .../__snapshots__/SampleDataBox.test.tsx.snap | 2 +- .../containers/AnomalyDetectionOverview.tsx | 13 ++-- .../AnomalyDetectionOverview.test.tsx | 33 ++++++--- .../AnomalyDetectionOverview.test.tsx.snap | 11 ++- .../containers/ReviewAndCreate.tsx | 24 ++++--- .../__tests__/ReviewAndCreate.test.tsx | 8 +++ public/pages/utils/helpers.ts | 4 +- public/plugin.ts | 6 +- .../reducers/__tests__/anomalyResults.test.ts | 11 ++- public/services.ts | 8 ++- public/utils/constants.ts | 9 +++ server/plugin.ts | 20 +++--- server/routes/ad.ts | 2 +- server/utils/helpers.ts | 5 +- 39 files changed, 536 insertions(+), 185 deletions(-) diff --git a/package.json b/package.json index 97b79f4d..c5b88cd0 100644 --- a/package.json +++ b/package.json @@ -56,4 +56,4 @@ "browserify-sign": "^4.2.2", "axios": "^1.6.1" } -} \ No newline at end of file +} diff --git a/public/pages/AnomalyCharts/containers/__tests__/AnomaliesChart.test.tsx b/public/pages/AnomalyCharts/containers/__tests__/AnomaliesChart.test.tsx index aebc1ea2..02f63fcd 100644 --- a/public/pages/AnomalyCharts/containers/__tests__/AnomaliesChart.test.tsx +++ b/public/pages/AnomalyCharts/containers/__tests__/AnomaliesChart.test.tsx @@ -22,6 +22,8 @@ import { FAKE_ANOMALIES_RESULT, FAKE_DATE_RANGE, } from '../../../../pages/utils/__tests__/constants'; +import { Router } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; const DEFAULT_PROPS = { onDateRangeChange: jest.fn(), @@ -47,17 +49,32 @@ const DEFAULT_PROPS = { entityAnomalySummaries: [], } as AnomaliesChartProps; +const history = createMemoryHistory(); + const renderDataFilter = (chartProps: AnomaliesChartProps) => ({ ...render( - + + + ), }); describe(' spec', () => { + beforeAll(() => { + Object.defineProperty(window, 'location', { + value: { + href: 'http://test.com', + pathname: '/', + search: '', + hash: '', + }, + writable: true + }); + }); test('renders the component for sample / preview', () => { console.error = jest.fn(); const { getByText, getAllByText } = renderDataFilter({ diff --git a/public/pages/AnomalyCharts/containers/__tests__/AnomalyDetailsChart.test.tsx b/public/pages/AnomalyCharts/containers/__tests__/AnomalyDetailsChart.test.tsx index fbf1e66d..5bed2db9 100644 --- a/public/pages/AnomalyCharts/containers/__tests__/AnomalyDetailsChart.test.tsx +++ b/public/pages/AnomalyCharts/containers/__tests__/AnomalyDetailsChart.test.tsx @@ -22,6 +22,18 @@ import { } from '../../../../pages/utils/__tests__/constants'; import { INITIAL_ANOMALY_SUMMARY } from '../../utils/constants'; import { getRandomDetector } from '../../../../redux/reducers/__tests__/utils'; +import { createMemoryHistory } from 'history'; +import { Router } from 'react-router-dom'; + +jest.mock('../../../../services', () => ({ + ...jest.requireActual('../../../../services'), + + getDataSourceEnabled: () => ({ + enabled: false + }) +})); + +const history = createMemoryHistory(); const renderAnomalyOccurenceChart = ( isNotSample: boolean, @@ -30,27 +42,41 @@ const renderAnomalyOccurenceChart = ( ...render( - + + + ), }); describe(' spec', () => { + beforeAll(() => { + Object.defineProperty(window, 'location', { + value: { + href: 'http://test.com', + pathname: '/', + search: '', + hash: '', + }, + writable: true + }); + }); + test('renders the component in case of Sample Anomaly', () => { console.error = jest.fn(); const { getByText } = renderAnomalyOccurenceChart(false, false); diff --git a/public/pages/AnomalyCharts/containers/__tests__/AnomalyOccurrenceChart.test.tsx b/public/pages/AnomalyCharts/containers/__tests__/AnomalyOccurrenceChart.test.tsx index 802d7d6c..94b4ffd4 100644 --- a/public/pages/AnomalyCharts/containers/__tests__/AnomalyOccurrenceChart.test.tsx +++ b/public/pages/AnomalyCharts/containers/__tests__/AnomalyOccurrenceChart.test.tsx @@ -16,6 +16,8 @@ import { mockedStore } from '../../../../redux/utils/testUtils'; import { CoreServicesContext } from '../../../../components/CoreServices/CoreServices'; import { coreServicesMock } from '../../../../../test/mocks'; import { AnomalyOccurrenceChart } from '../AnomalyOccurrenceChart'; +import { createMemoryHistory } from 'history'; +import { Router } from 'react-router-dom'; import { FAKE_ANOMALY_DATA, FAKE_DATE_RANGE, @@ -23,6 +25,16 @@ import { import { INITIAL_ANOMALY_SUMMARY } from '../../utils/constants'; import { getRandomDetector } from '../../../../redux/reducers/__tests__/utils'; +jest.mock('../../../../services', () => ({ + ...jest.requireActual('../../../../services'), + + getDataSourceEnabled: () => ({ + enabled: false + }) +})); + +const history = createMemoryHistory(); + const renderAnomalyOccurenceChart = ( isNotSample: boolean, isHCDetector: boolean @@ -30,28 +42,41 @@ const renderAnomalyOccurenceChart = ( ...render( - + + + ), }); describe(' spec', () => { + beforeAll(() => { + Object.defineProperty(window, 'location', { + value: { + href: 'http://test.com', + pathname: '/', + search: '', + hash: '', + }, + writable: true + }); + }); test('renders the component in case of Sample Anomaly', () => { console.error = jest.fn(); const { getByText } = renderAnomalyOccurenceChart(false, false); diff --git a/public/pages/ConfigureModel/containers/ConfigureModel.tsx b/public/pages/ConfigureModel/containers/ConfigureModel.tsx index 7a43f6bd..6d2996e8 100644 --- a/public/pages/ConfigureModel/containers/ConfigureModel.tsx +++ b/public/pages/ConfigureModel/containers/ConfigureModel.tsx @@ -32,7 +32,7 @@ import { RouteComponentProps, useLocation } from 'react-router-dom'; import { AppState } from '../../../redux/reducers'; import { getMappings } from '../../../redux/reducers/opensearch'; import { useFetchDetectorInfo } from '../../CreateDetectorSteps/hooks/useFetchDetectorInfo'; -import { BREADCRUMBS, BASE_DOCS_LINK } from '../../../utils/constants'; +import { BREADCRUMBS, BASE_DOCS_LINK, MDS_BREADCRUMBS } from '../../../utils/constants'; import { useHideSideNavBar } from '../../main/hooks/useHideSideNavBar'; import { updateDetector } from '../../../redux/reducers/ad'; import { @@ -64,7 +64,7 @@ import { } from '../../../pages/utils/helpers'; import { getDataSourceManagementPlugin, - getDataSourcePlugin, + getDataSourceEnabled, getNotifications, getSavedObjectsClient, } from '../../../services'; @@ -89,7 +89,7 @@ export function ConfigureModel(props: ConfigureModelProps) { const dispatch = useDispatch(); const location = useLocation(); const MDSQueryParams = getDataSourceFromURL(location); - const dataSourceEnabled = getDataSourcePlugin()?.dataSourceEnabled || false; + const dataSourceEnabled = getDataSourceEnabled().enabled; const dataSourceId = MDSQueryParams.dataSourceId; useHideSideNavBar(true, false); @@ -122,30 +122,55 @@ export function ConfigureModel(props: ConfigureModelProps) { }, [detector]); useEffect(() => { - if (props.isEdit) { - core.chrome.setBreadcrumbs([ - BREADCRUMBS.ANOMALY_DETECTOR, - BREADCRUMBS.DETECTORS, - { - text: detector && detector.name ? detector.name : '', - href: constructHrefWithDataSourceId(`#/detectors/${detectorId}`, dataSourceId, false) - }, - BREADCRUMBS.EDIT_MODEL_CONFIGURATION, - ]); + if (dataSourceEnabled) { + if (props.isEdit) { + core.chrome.setBreadcrumbs([ + MDS_BREADCRUMBS.ANOMALY_DETECTOR(dataSourceId), + MDS_BREADCRUMBS.DETECTORS(dataSourceId), + { + text: detector && detector.name ? detector.name : '', + href: constructHrefWithDataSourceId(`#/detectors/${detectorId}`, dataSourceId, false) + }, + MDS_BREADCRUMBS.EDIT_MODEL_CONFIGURATION, + ]); + } else { + core.chrome.setBreadcrumbs([ + MDS_BREADCRUMBS.ANOMALY_DETECTOR(dataSourceId), + MDS_BREADCRUMBS.DETECTORS(dataSourceId), + MDS_BREADCRUMBS.CREATE_DETECTOR, + ]); + } } else { - core.chrome.setBreadcrumbs([ - BREADCRUMBS.ANOMALY_DETECTOR, - BREADCRUMBS.DETECTORS, - BREADCRUMBS.CREATE_DETECTOR, - ]); + if (props.isEdit) { + core.chrome.setBreadcrumbs([ + BREADCRUMBS.ANOMALY_DETECTOR, + BREADCRUMBS.DETECTORS, + { + text: detector && detector.name ? detector.name : '', + href: `#/detectors/${detectorId}`, + }, + BREADCRUMBS.EDIT_MODEL_CONFIGURATION, + ]); + } else { + core.chrome.setBreadcrumbs([ + BREADCRUMBS.ANOMALY_DETECTOR, + BREADCRUMBS.DETECTORS, + BREADCRUMBS.CREATE_DETECTOR, + ]); + } } }, [detector]); useEffect(() => { if (hasError) { - props.history.push( - constructHrefWithDataSourceId('/detectors', dataSourceId, false) - ); + if(dataSourceEnabled) { + props.history.push( + constructHrefWithDataSourceId('/detectors', dataSourceId, false) + ); + } + else { + props.history.push('/detectors'); + } } }, [hasError]); diff --git a/public/pages/ConfigureModel/containers/__tests__/ConfigureModel.test.tsx b/public/pages/ConfigureModel/containers/__tests__/ConfigureModel.test.tsx index 77a9e370..6cd845bd 100644 --- a/public/pages/ConfigureModel/containers/__tests__/ConfigureModel.test.tsx +++ b/public/pages/ConfigureModel/containers/__tests__/ConfigureModel.test.tsx @@ -25,6 +25,14 @@ import { CoreServicesContext } from '../../../../components/CoreServices/CoreSer import { INITIAL_DETECTOR_DEFINITION_VALUES } from '../../../DefineDetector/utils/constants'; import { INITIAL_MODEL_CONFIGURATION_VALUES } from '../../utils/constants'; +jest.mock('../../../../services', () => ({ + ...jest.requireActual('../../../../services'), + + getDataSourceEnabled: () => ({ + enabled: false + }) +})); + const renderWithRouter = (isEdit: boolean = false) => ({ ...render( @@ -34,6 +42,7 @@ const renderWithRouter = (isEdit: boolean = false) => ({ render={(props: RouteComponentProps) => ( ({ + ...jest.requireActual('../../../../../services'), + + getDataSourceEnabled: () => ({ + enabled: false + }) +})); + +const history = createMemoryHistory(); describe(' spec', () => { describe('Empty results', () => { test('renders component with empty message', async () => { - const { container } = render(); + const { container } = render( + + + ); expect(container.firstChild).toMatchSnapshot(); }); }); 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 67762b0d..e71ff3ba 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 @@ -72,7 +72,7 @@ exports[` spec Empty results renders component with empty messa @@ -93,7 +93,7 @@ exports[` spec Empty results renders component with empty messa diff --git a/public/pages/Dashboard/Components/__tests__/AnomaliesLiveCharts.test.tsx b/public/pages/Dashboard/Components/__tests__/AnomaliesLiveCharts.test.tsx index 23f65912..fd043c49 100644 --- a/public/pages/Dashboard/Components/__tests__/AnomaliesLiveCharts.test.tsx +++ b/public/pages/Dashboard/Components/__tests__/AnomaliesLiveCharts.test.tsx @@ -11,6 +11,17 @@ import { Provider } from 'react-redux'; import { coreServicesMock } from '../../../../../test/mocks'; import { CoreServicesContext } from '../../../../components/CoreServices/CoreServices'; import { mockedStore } from '../../../../redux/utils/testUtils'; +import { createMemoryHistory } from 'history'; +import { Router } from 'react-router-dom'; + +jest.mock('../../../../services', () => ({ + ...jest.requireActual('../../../../services'), + + getDataSourceEnabled: () => ({ + enabled: false + }) +})); + const anomalyResponse = [ { ok: true, @@ -41,11 +52,26 @@ jest.mock('../../utils/utils', () => ({ visualizeAnomalyResultForXYChart: jest.fn(), })); describe(' spec', () => { + beforeAll(() => { + Object.defineProperty(window, 'location', { + value: { + href: 'http://test.com', + pathname: '/', + search: '', + hash: '', + }, + writable: true + }); + }); test('AnomaliesLiveChart with Sample anomaly data', async () => { + const history = createMemoryHistory(); + const { container, getByTestId, getAllByText, getByText } = render( - + + + ); diff --git a/public/pages/Dashboard/Container/DashboardOverview.tsx b/public/pages/Dashboard/Container/DashboardOverview.tsx index 6478f0ad..d1728536 100644 --- a/public/pages/Dashboard/Container/DashboardOverview.tsx +++ b/public/pages/Dashboard/Container/DashboardOverview.tsx @@ -44,7 +44,7 @@ import { getDataSourceFromURL, getVisibleOptions, } from '../../utils/helpers'; -import { BREADCRUMBS } from '../../../utils/constants'; +import { BREADCRUMBS, MDS_BREADCRUMBS } from '../../../utils/constants'; import { DETECTOR_STATE } from '../../../../server/utils/constants'; import { getDetectorStateOptions, @@ -60,7 +60,7 @@ import { CoreStart, MountPoint } from '../../../../../../src/core/public'; import { DataSourceSelectableConfig } from '../../../../../../src/plugins/data_source_management/public'; import { getDataSourceManagementPlugin, - getDataSourcePlugin, + getDataSourceEnabled, getNotifications, getSavedObjectsClient, } from '../../../services'; @@ -79,7 +79,7 @@ export function DashboardOverview(props: OverviewProps) { const errorGettingDetectors = adState.errorMessage; const isLoadingDetectors = adState.requesting; - const dataSourceEnabled = getDataSourcePlugin()?.dataSourceEnabled || false; + const dataSourceEnabled = getDataSourceEnabled().enabled; const [currentDetectors, setCurrentDetectors] = useState( Object.values(allDetectorList) @@ -141,7 +141,7 @@ export function DashboardOverview(props: OverviewProps) { const handleDataSourceChange = ([event]) => { const dataSourceId = event?.id; - if (!dataSourceId) { + if (dataSourceEnabled && dataSourceId === undefined) { getNotifications().toasts.addDanger( prettifyErrorMessage('Unable to set data source.') ); @@ -238,10 +238,17 @@ export function DashboardOverview(props: OverviewProps) { }, [errorGettingDetectors]); useEffect(() => { - core.chrome.setBreadcrumbs([ - BREADCRUMBS.ANOMALY_DETECTOR, - BREADCRUMBS.DASHBOARD, - ]); + if (dataSourceEnabled) { + core.chrome.setBreadcrumbs([ + MDS_BREADCRUMBS.ANOMALY_DETECTOR(MDSOverviewState.selectedDataSourceId), + MDS_BREADCRUMBS.DASHBOARD(MDSOverviewState.selectedDataSourceId), + ]); + } else { + core.chrome.setBreadcrumbs([ + BREADCRUMBS.ANOMALY_DETECTOR, + BREADCRUMBS.DASHBOARD, + ]); + } }); useEffect(() => { diff --git a/public/pages/DefineDetector/components/DataFilterList/components/__tests__/DataFilter.test.tsx b/public/pages/DefineDetector/components/DataFilterList/components/__tests__/DataFilter.test.tsx index 4eb5f4cd..2b42c5e1 100644 --- a/public/pages/DefineDetector/components/DataFilterList/components/__tests__/DataFilter.test.tsx +++ b/public/pages/DefineDetector/components/DataFilterList/components/__tests__/DataFilter.test.tsx @@ -150,7 +150,7 @@ describe('dataFilter', () => { getAllByText('cpu'); }); userEvent.click(getByTestId('cancelFilter0Button')); - }, 10000); + }, 30000); test('renders data filter, click on custom', async () => { const { container, getByText, getByTestId } = renderWithProvider(); getByText('Create custom label?'); @@ -164,5 +164,5 @@ describe('dataFilter', () => { getByText('Use query DSL'); }); userEvent.click(getByTestId('cancelFilter0Button')); - }, 10000); + }, 30000); }); diff --git a/public/pages/DefineDetector/containers/DefineDetector.tsx b/public/pages/DefineDetector/containers/DefineDetector.tsx index ee4e795d..21d6ccba 100644 --- a/public/pages/DefineDetector/containers/DefineDetector.tsx +++ b/public/pages/DefineDetector/containers/DefineDetector.tsx @@ -39,7 +39,7 @@ import { useFetchDetectorInfo } from '../../CreateDetectorSteps/hooks/useFetchDe import { CoreStart, MountPoint } from '../../../../../../src/core/public'; import { APIAction } from '../../../redux/middleware/types'; import { CoreServicesContext } from '../../../components/CoreServices/CoreServices'; -import { BREADCRUMBS, DATA_SOURCE_ID } from '../../../utils/constants'; +import { BREADCRUMBS, MDS_BREADCRUMBS } from '../../../utils/constants'; import { getErrorMessage, validateDetectorName } from '../../../utils/utils'; import { NameAndDescription } from '../components/NameAndDescription'; import { DataSource } from '../components/Datasource/DataSource'; @@ -58,7 +58,7 @@ import { DETECTOR_STATE } from '../../../../server/utils/constants'; import { ModelConfigurationFormikValues } from 'public/pages/ConfigureModel/models/interfaces'; import { getDataSourceManagementPlugin, - getDataSourcePlugin, + getDataSourceEnabled, getNotifications, getSavedObjectsClient, } from '../../../services'; @@ -87,7 +87,7 @@ export const DefineDetector = (props: DefineDetectorProps) => { const location = useLocation(); const MDSQueryParams = getDataSourceFromURL(location); const dataSourceId = MDSQueryParams.dataSourceId; - const dataSourceEnabled = getDataSourcePlugin()?.dataSourceEnabled || false; + const dataSourceEnabled = getDataSourceEnabled().enabled; const core = React.useContext(CoreServicesContext) as CoreStart; const dispatch = useDispatch>(); @@ -126,22 +126,41 @@ export const DefineDetector = (props: DefineDetectorProps) => { // Set breadcrumbs based on create / update useEffect(() => { - const createOrEditBreadcrumb = props.isEdit - ? BREADCRUMBS.EDIT_DETECTOR - : BREADCRUMBS.CREATE_DETECTOR; - let breadCrumbs = [ - BREADCRUMBS.ANOMALY_DETECTOR, - BREADCRUMBS.DETECTORS, - createOrEditBreadcrumb, - ]; - if (detector && detector.name) { - breadCrumbs.splice(2, 0, { - text: detector.name, - //@ts-ignore - href: `#/detectors/${detectorId}`, - }); + if (dataSourceEnabled) { + const createOrEditBreadcrumb = props.isEdit + ? MDS_BREADCRUMBS.EDIT_DETECTOR + : MDS_BREADCRUMBS.CREATE_DETECTOR; + let breadCrumbs = [ + MDS_BREADCRUMBS.ANOMALY_DETECTOR(dataSourceId), + MDS_BREADCRUMBS.DETECTORS(dataSourceId), + createOrEditBreadcrumb, + ]; + if (detector && detector.name) { + breadCrumbs.splice(2, 0, { + text: detector.name, + //@ts-ignore + href: `#/detectors/${detectorId}/?dataSourceId=${dataSourceId}`, + }); + } + core.chrome.setBreadcrumbs(breadCrumbs); + } else { + const createOrEditBreadcrumb = props.isEdit + ? BREADCRUMBS.EDIT_DETECTOR + : BREADCRUMBS.CREATE_DETECTOR; + let breadCrumbs = [ + BREADCRUMBS.ANOMALY_DETECTOR, + BREADCRUMBS.DETECTORS, + createOrEditBreadcrumb, + ]; + if (detector && detector.name) { + breadCrumbs.splice(2, 0, { + text: detector.name, + //@ts-ignore + href: `#/detectors/${detectorId}`, + }); + } + core.chrome.setBreadcrumbs(breadCrumbs); } - core.chrome.setBreadcrumbs(breadCrumbs); }); // If no detector found with ID, redirect it to list @@ -258,7 +277,7 @@ export const DefineDetector = (props: DefineDetectorProps) => { const handleDataSourceChange = ([event]) => { const dataSourceId = event?.id; - if (!dataSourceId) { + if (dataSourceEnabled && dataSourceId === undefined) { getNotifications().toasts.addDanger( prettifyErrorMessage('Unable to set data source.') ); diff --git a/public/pages/DefineDetector/containers/__tests__/DefineDetector.test.tsx b/public/pages/DefineDetector/containers/__tests__/DefineDetector.test.tsx index 84f9a212..0ba9421f 100644 --- a/public/pages/DefineDetector/containers/__tests__/DefineDetector.test.tsx +++ b/public/pages/DefineDetector/containers/__tests__/DefineDetector.test.tsx @@ -27,6 +27,14 @@ import { testDetectorDefinitionValues, } from '../../utils/constants'; +jest.mock('../../../../services', () => ({ + ...jest.requireActual('../../../../services'), + + getDataSourceEnabled: () => ({ + enabled: false + }) +})); + const renderWithRouterEmpty = (isEdit: boolean = false) => ({ ...render( @@ -36,6 +44,7 @@ const renderWithRouterEmpty = (isEdit: boolean = false) => ({ render={(props: RouteComponentProps) => ( ({ render={(props: RouteComponentProps) => ( { const core = React.useContext(CoreServicesContext) as CoreStart; const dispatch = useDispatch(); const detectorId = get(props, 'match.params.detectorId', '') as string; - const dataSourceEnabled = getDataSourcePlugin()?.dataSourceEnabled || false; + const dataSourceEnabled = getDataSourceEnabled().enabled; const location = useLocation(); const MDSQueryParams = getDataSourceFromURL(location); const dataSourceId = MDSQueryParams.dataSourceId; @@ -203,11 +203,19 @@ export const DetectorDetail = (props: DetectorDetailProps) => { useEffect(() => { if (detector) { - core.chrome.setBreadcrumbs([ - BREADCRUMBS.ANOMALY_DETECTOR, - BREADCRUMBS.DETECTORS, - { text: detector ? detector.name : '' }, - ]); + if(dataSourceEnabled) { + core.chrome.setBreadcrumbs([ + MDS_BREADCRUMBS.ANOMALY_DETECTOR(dataSourceId), + MDS_BREADCRUMBS.DETECTORS(dataSourceId), + { text: detector ? detector.name : '' }, + ]); + } else { + core.chrome.setBreadcrumbs([ + BREADCRUMBS.ANOMALY_DETECTOR, + BREADCRUMBS.DETECTORS, + { text: detector ? detector.name : '' }, + ]); + } } }, [detector]); diff --git a/public/pages/DetectorJobs/containers/DetectorJobs.tsx b/public/pages/DetectorJobs/containers/DetectorJobs.tsx index e919ae53..435b4dc8 100644 --- a/public/pages/DetectorJobs/containers/DetectorJobs.tsx +++ b/public/pages/DetectorJobs/containers/DetectorJobs.tsx @@ -39,7 +39,7 @@ import { } from '../../../pages/utils/helpers'; import { getDataSourceManagementPlugin, - getDataSourcePlugin, + getDataSourceEnabled, getNotifications, getSavedObjectsClient, } from '../../../services'; @@ -56,7 +56,7 @@ export function DetectorJobs(props: DetectorJobsProps) { const core = React.useContext(CoreServicesContext) as CoreStart; const location = useLocation(); const MDSQueryParams = getDataSourceFromURL(location); - const dataSourceEnabled = getDataSourcePlugin()?.dataSourceEnabled || false; + const dataSourceEnabled = getDataSourceEnabled().enabled; const dataSourceId = MDSQueryParams.dataSourceId; useHideSideNavBar(true, false); @@ -73,11 +73,19 @@ export function DetectorJobs(props: DetectorJobsProps) { }, []); useEffect(() => { - core.chrome.setBreadcrumbs([ - BREADCRUMBS.ANOMALY_DETECTOR, - BREADCRUMBS.DETECTORS, - BREADCRUMBS.CREATE_DETECTOR, - ]); + if (dataSourceEnabled) { + core.chrome.setBreadcrumbs([ + BREADCRUMBS.ANOMALY_DETECTOR, + BREADCRUMBS.DETECTORS, + BREADCRUMBS.CREATE_DETECTOR, + ]); + } else { + core.chrome.setBreadcrumbs([ + BREADCRUMBS.ANOMALY_DETECTOR, + BREADCRUMBS.DETECTORS, + BREADCRUMBS.CREATE_DETECTOR, + ]); + } }, []); const handleFormValidation = async ( diff --git a/public/pages/DetectorJobs/containers/__tests__/DetectorJobs.test.tsx b/public/pages/DetectorJobs/containers/__tests__/DetectorJobs.test.tsx index ac8e9b7b..cea842df 100644 --- a/public/pages/DetectorJobs/containers/__tests__/DetectorJobs.test.tsx +++ b/public/pages/DetectorJobs/containers/__tests__/DetectorJobs.test.tsx @@ -24,6 +24,14 @@ import { httpClientMock, coreServicesMock } from '../../../../../test/mocks'; import { CoreServicesContext } from '../../../../components/CoreServices/CoreServices'; import { INITIAL_DETECTOR_JOB_VALUES } from '../../utils/constants'; +jest.mock('../../../../services', () => ({ + ...jest.requireActual('../../../../services'), + + getDataSourceEnabled: () => ({ + enabled: false + }) +})); + const renderWithRouter = (isEdit: boolean = false) => ({ ...render( diff --git a/public/pages/DetectorResults/containers/AnomalyResults.tsx b/public/pages/DetectorResults/containers/AnomalyResults.tsx index 4d869961..b09b18b2 100644 --- a/public/pages/DetectorResults/containers/AnomalyResults.tsx +++ b/public/pages/DetectorResults/containers/AnomalyResults.tsx @@ -34,6 +34,7 @@ import { BREADCRUMBS, DATA_SOURCE_ID, FEATURE_DATA_POINTS_WINDOW, + MDS_BREADCRUMBS, MISSING_FEATURE_DATA_SEVERITY, } from '../../../utils/constants'; import { DETECTOR_STATE } from '../../../../server/utils/constants'; @@ -69,6 +70,7 @@ import { CoreStart } from '../../../../../../src/core/public'; import { CoreServicesContext } from '../../../components/CoreServices/CoreServices'; import { DEFAULT_SHINGLE_SIZE } from '../../../utils/constants'; import { getDataSourceFromURL } from '../../../pages/utils/helpers'; +import { getDataSourceEnabled } from '../../../services'; interface AnomalyResultsProps extends RouteComponentProps { detectorId: string; @@ -87,14 +89,23 @@ export function AnomalyResults(props: AnomalyResultsProps) { ); const location = useLocation(); const MDSQueryParams = getDataSourceFromURL(location); + const dataSourceEnabled = getDataSourceEnabled().enabled; const dataSourceId = MDSQueryParams.dataSourceId; useEffect(() => { - core.chrome.setBreadcrumbs([ - BREADCRUMBS.ANOMALY_DETECTOR, - BREADCRUMBS.DETECTORS, - { text: detector ? detector.name : '' }, - ]); + if (dataSourceEnabled) { + core.chrome.setBreadcrumbs([ + MDS_BREADCRUMBS.ANOMALY_DETECTOR(dataSourceId), + MDS_BREADCRUMBS.DETECTORS(dataSourceId), + { text: detector ? detector.name : '' }, + ]); + } else { + core.chrome.setBreadcrumbs([ + BREADCRUMBS.ANOMALY_DETECTOR, + BREADCRUMBS.DETECTORS, + { text: detector ? detector.name : '' }, + ]); + } dispatch(getDetector(detectorId, dataSourceId)); }, []); diff --git a/public/pages/DetectorsList/components/EmptyMessage/__tests__/EmptyMessage.test.tsx b/public/pages/DetectorsList/components/EmptyMessage/__tests__/EmptyMessage.test.tsx index db9cfebf..af8c0feb 100644 --- a/public/pages/DetectorsList/components/EmptyMessage/__tests__/EmptyMessage.test.tsx +++ b/public/pages/DetectorsList/components/EmptyMessage/__tests__/EmptyMessage.test.tsx @@ -11,16 +11,41 @@ import React from 'react'; import { render, fireEvent } from '@testing-library/react'; +import { Router } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; import { EmptyDetectorMessage } from '../EmptyMessage'; +jest.mock('../../../../../services', () => ({ + ...jest.requireActual('../../../../../services'), + + getDataSourceEnabled: () => ({ + enabled: false + }) +})); + describe(' spec', () => { + beforeAll(() => { + Object.defineProperty(window, 'location', { + value: { + href: 'http://test.com', + pathname: '/', + search: '', + hash: '', + }, + writable: true + }); + }); describe('Empty results', () => { test('renders component with empty message', () => { + const history = createMemoryHistory(); + const { container, getByText } = render( - + + + ); expect(container.firstChild).toMatchSnapshot(); getByText('Create detector'); diff --git a/public/pages/DetectorsList/components/EmptyMessage/__tests__/__snapshots__/EmptyMessage.test.tsx.snap b/public/pages/DetectorsList/components/EmptyMessage/__tests__/__snapshots__/EmptyMessage.test.tsx.snap index ed30a454..a429c5f9 100644 --- a/public/pages/DetectorsList/components/EmptyMessage/__tests__/__snapshots__/EmptyMessage.test.tsx.snap +++ b/public/pages/DetectorsList/components/EmptyMessage/__tests__/__snapshots__/EmptyMessage.test.tsx.snap @@ -33,7 +33,7 @@ exports[` spec Empty results renders component with empt @@ -54,7 +54,7 @@ exports[` spec Empty results renders component with empt diff --git a/public/pages/DetectorsList/containers/ConfirmActionModals/__tests__/ConfirmDeleteDetectorsModal.test.tsx b/public/pages/DetectorsList/containers/ConfirmActionModals/__tests__/ConfirmDeleteDetectorsModal.test.tsx index fa9987e4..1af99b47 100644 --- a/public/pages/DetectorsList/containers/ConfirmActionModals/__tests__/ConfirmDeleteDetectorsModal.test.tsx +++ b/public/pages/DetectorsList/containers/ConfirmActionModals/__tests__/ConfirmDeleteDetectorsModal.test.tsx @@ -82,7 +82,7 @@ describe(' spec', () => { expect(defaultDeleteProps.onStopDetectors).not.toHaveBeenCalled(); expect(defaultDeleteProps.onDeleteDetectors).not.toHaveBeenCalled(); expect(defaultDeleteProps.onConfirm).not.toHaveBeenCalled(); - }, 10000); + }, 5000); test('should have delete button enabled if delete typed', async () => { const { getByTestId, getByPlaceholderText } = render( @@ -93,7 +93,7 @@ describe(' spec', () => { userEvent.click(getByTestId('confirmButton')); await waitFor(() => {}); expect(defaultDeleteProps.onConfirm).toHaveBeenCalled(); - }, 10000); + }, 5000); test('should not show callout if no detectors are running', async () => { const { queryByText } = render( diff --git a/public/pages/DetectorsList/containers/List/List.tsx b/public/pages/DetectorsList/containers/List/List.tsx index e98d10eb..366ac3fe 100644 --- a/public/pages/DetectorsList/containers/List/List.tsx +++ b/public/pages/DetectorsList/containers/List/List.tsx @@ -42,7 +42,7 @@ import { getIndices, getPrioritizedIndices, } from '../../../../redux/reducers/opensearch'; -import { APP_PATH, PLUGIN_NAME } from '../../../../utils/constants'; +import { APP_PATH, MDS_BREADCRUMBS, PLUGIN_NAME } from '../../../../utils/constants'; import { DETECTOR_STATE } from '../../../../../server/utils/constants'; import { constructHrefWithDataSourceId, @@ -87,7 +87,7 @@ import { CoreServicesContext } from '../../../../components/CoreServices/CoreSer import { DataSourceSelectableConfig } from '../../../../../../../src/plugins/data_source_management/public'; import { getDataSourceManagementPlugin, - getDataSourcePlugin, + getDataSourceEnabled, getNotifications, getSavedObjectsClient, } from '../../../../services'; @@ -138,7 +138,7 @@ export const DetectorList = (props: ListProps) => { (state: AppState) => state.ad.requesting ); - const dataSourceEnabled = getDataSourcePlugin()?.dataSourceEnabled || false; + const dataSourceEnabled = getDataSourceEnabled().enabled; const [selectedDetectors, setSelectedDetectors] = useState( [] as DetectorListItem[] @@ -213,10 +213,17 @@ export const DetectorList = (props: ListProps) => { // Set breadcrumbs on page initialization useEffect(() => { - core.chrome.setBreadcrumbs([ - BREADCRUMBS.ANOMALY_DETECTOR, - BREADCRUMBS.DETECTORS, - ]); + if (dataSourceEnabled) { + core.chrome.setBreadcrumbs([ + BREADCRUMBS.ANOMALY_DETECTOR, + BREADCRUMBS.DETECTORS, + ]); + } else { + core.chrome.setBreadcrumbs([ + MDS_BREADCRUMBS.ANOMALY_DETECTOR(state.selectedDataSourceId), + MDS_BREADCRUMBS.DETECTORS(state.selectedDataSourceId), + ]); + } }, []); // Getting all initial indices @@ -583,7 +590,8 @@ export const DetectorList = (props: ListProps) => { const handleDataSourceChange = ([event]) => { const dataSourceId = event?.id; - if (!dataSourceId) { + + if (dataSourceEnabled && dataSourceId === undefined) { getNotifications().toasts.addDanger( prettifyErrorMessage('Unable to set data source.') ); diff --git a/public/pages/DetectorsList/containers/List/__tests__/List.test.tsx b/public/pages/DetectorsList/containers/List/__tests__/List.test.tsx index e932f4b2..3bc8c268 100644 --- a/public/pages/DetectorsList/containers/List/__tests__/List.test.tsx +++ b/public/pages/DetectorsList/containers/List/__tests__/List.test.tsx @@ -31,6 +31,14 @@ import { DetectorList, ListRouterParams } from '../List'; import { DETECTOR_STATE } from '../../../../../../server/utils/constants'; import { CoreServicesContext } from '../../../../../components/CoreServices/CoreServices'; +jest.mock('../../../../../services', () => ({ + ...jest.requireActual('../../../../../services'), + + getDataSourceEnabled: () => ({ + enabled: false + }) +})); + const renderWithRouter = ( initialAdState: Detectors = initialDetectorsState ) => ({ @@ -43,7 +51,9 @@ const renderWithRouter = ( path="/detectors" render={(props: RouteComponentProps) => ( - + )} /> diff --git a/public/pages/DetectorsList/utils/__tests__/helpers.test.ts b/public/pages/DetectorsList/utils/__tests__/helpers.test.ts index 0cf68c84..a0c8f07d 100644 --- a/public/pages/DetectorsList/utils/__tests__/helpers.test.ts +++ b/public/pages/DetectorsList/utils/__tests__/helpers.test.ts @@ -30,6 +30,7 @@ describe('helpers spec', () => { indices: '', sortField: 'name', sortDirection: SORT_DIRECTION.ASC, + dataSourceId: '', }); }); test('should default values if missing from queryParams', () => { @@ -42,6 +43,7 @@ describe('helpers spec', () => { indices: '', sortField: 'name', sortDirection: SORT_DIRECTION.ASC, + dataSourceId:'', }); }); test('should return queryParams from location', () => { @@ -57,6 +59,7 @@ describe('helpers spec', () => { indices: 'someIndex', sortField: 'name', sortDirection: SORT_DIRECTION.DESC, + dataSourceId:'', }); }); }); diff --git a/public/pages/DetectorsList/utils/__tests__/tableUtils.test.tsx b/public/pages/DetectorsList/utils/__tests__/tableUtils.test.tsx index 327767dc..9496cd2a 100644 --- a/public/pages/DetectorsList/utils/__tests__/tableUtils.test.tsx +++ b/public/pages/DetectorsList/utils/__tests__/tableUtils.test.tsx @@ -9,43 +9,43 @@ * GitHub history for details. */ -import { staticColumn } from '../../utils/tableUtils'; +import { getColumns } from '../../utils/tableUtils'; import { render } from '@testing-library/react'; describe('tableUtils spec', () => { describe('should render the column titles', () => { test('detector name column', () => { - const result = staticColumn; + const result = getColumns(''); const { getByText } = render(result[0].name); getByText('Detector'); }); test('indices column', () => { - const result = staticColumn; + const result = getColumns(''); const { getByText } = render(result[1].name); getByText('Indices'); }); test('detector state column', () => { - const result = staticColumn; + const result = getColumns(''); const { getByText } = render(result[2].name); getByText('Real-time state'); }); test('historical analysis column', () => { - const result = staticColumn; + const result = getColumns(''); const { getByText } = render(result[3].name); getByText('Historical analysis'); }); test('anomalies last 24 hrs column', () => { - const result = staticColumn; + const result = getColumns(''); const { getByText } = render(result[4].name); getByText('Anomalies last 24 hours'); }); test('last RT occurrence column', () => { - const result = staticColumn; + const result = getColumns(''); const { getByText } = render(result[5].name); getByText('Last real-time occurrence'); }); test('last started time column', () => { - const result = staticColumn; + const result = getColumns(''); const { getByText } = render(result[6].name); getByText('Last started'); }); diff --git a/public/pages/Overview/components/SampleDataBox/__tests__/SampleDataBox.test.tsx b/public/pages/Overview/components/SampleDataBox/__tests__/SampleDataBox.test.tsx index ef32c66b..d5afb538 100644 --- a/public/pages/Overview/components/SampleDataBox/__tests__/SampleDataBox.test.tsx +++ b/public/pages/Overview/components/SampleDataBox/__tests__/SampleDataBox.test.tsx @@ -13,6 +13,8 @@ import React from 'react'; import { render } from '@testing-library/react'; import { EuiIcon } from '@elastic/eui'; import { SampleDataBox } from '../SampleDataBox'; +import { Router } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; const defaultProps = { title: 'Sample title', @@ -25,11 +27,33 @@ const defaultProps = { detectorId: 'sample-detector-id', }; +jest.mock('../../../../../services', () => ({ + ...jest.requireActual('../../../../../services'), + + getDataSourceEnabled: () => ({ + enabled: false + }) +})); + describe(' spec', () => { + beforeAll(() => { + Object.defineProperty(window, 'location', { + value: { + href: 'http://test.com', + pathname: '/', + search: '', + hash: '', + }, + writable: true + }); + }); describe('Data not loaded', () => { test('renders component', () => { + const history = createMemoryHistory(); const { container, getByText } = render( - + + + ); expect(container.firstChild).toMatchSnapshot(); getByText('Sample title'); @@ -39,8 +63,13 @@ describe(' spec', () => { }); describe('Data is loading', () => { test('renders component', () => { + const history = createMemoryHistory(); const { container, getByText } = render( - + + + ); expect(container.firstChild).toMatchSnapshot(); getByText('Sample title'); @@ -50,8 +79,14 @@ describe(' spec', () => { }); describe('Data is loaded', () => { test('renders component', () => { + const history = createMemoryHistory(); + const { container, getByText } = render( - + + + ); expect(container.firstChild).toMatchSnapshot(); getByText('Sample title'); 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 fbec5767..4cdf707a 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 @@ -98,7 +98,7 @@ exports[` spec Data is loaded renders component 1`] = ` View detector and sample data diff --git a/public/pages/Overview/containers/AnomalyDetectionOverview.tsx b/public/pages/Overview/containers/AnomalyDetectionOverview.tsx index d6c00927..78aab2fc 100644 --- a/public/pages/Overview/containers/AnomalyDetectionOverview.tsx +++ b/public/pages/Overview/containers/AnomalyDetectionOverview.tsx @@ -28,6 +28,7 @@ import { BREADCRUMBS, PLUGIN_NAME, BASE_DOCS_LINK, + MDS_BREADCRUMBS, } from '../../../utils/constants'; import { SAMPLE_TYPE } from '../../../../server/utils/constants'; import { GET_SAMPLE_INDICES_QUERY } from '../../utils/constants'; @@ -58,7 +59,7 @@ import { CreateWorkflowStepSeparator } from '../components/CreateWorkflowStepSep import { DataSourceSelectableConfig } from '../../../../../../src/plugins/data_source_management/public'; import { getDataSourceManagementPlugin, - getDataSourcePlugin, + getDataSourceEnabled, getNotifications, getSavedObjectsClient, } from '../../../../public/services'; @@ -86,7 +87,7 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { const allSampleDetectors = Object.values( useSelector((state: AppState) => state.ad.detectorList) ); - const dataSourceEnabled = getDataSourcePlugin()?.dataSourceEnabled || false; + const dataSourceEnabled = getDataSourceEnabled().enabled; const [isLoadingHttpData, setIsLoadingHttpData] = useState(false); const [isLoadingEcommerceData, setIsLoadingEcommerceData] = useState(false); @@ -109,7 +110,11 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { // Set breadcrumbs on page initialization useEffect(() => { - core.chrome.setBreadcrumbs([BREADCRUMBS.ANOMALY_DETECTOR]); + if (dataSourceEnabled) { + core.chrome.setBreadcrumbs([MDS_BREADCRUMBS.ANOMALY_DETECTOR(MDSOverviewState.selectedDataSourceId)]); + } else { + core.chrome.setBreadcrumbs([BREADCRUMBS.ANOMALY_DETECTOR]); + } }, []); // Getting all initial sample detectors & indices @@ -219,7 +224,7 @@ export function AnomalyDetectionOverview(props: AnomalyDetectionOverviewProps) { const handleDataSourceChange = ([event]) => { const dataSourceId = event?.id; - if (!dataSourceId) { + if (dataSourceEnabled && dataSourceId === undefined) { getNotifications().toasts.addDanger( prettifyErrorMessage('Unable to set data source.') ); diff --git a/public/pages/Overview/containers/__tests__/AnomalyDetectionOverview.test.tsx b/public/pages/Overview/containers/__tests__/AnomalyDetectionOverview.test.tsx index 60536abc..88411861 100644 --- a/public/pages/Overview/containers/__tests__/AnomalyDetectionOverview.test.tsx +++ b/public/pages/Overview/containers/__tests__/AnomalyDetectionOverview.test.tsx @@ -18,17 +18,23 @@ import { Redirect, Route, Switch, + RouteComponentProps, } from 'react-router-dom'; import { httpClientMock, coreServicesMock } from '../../../../../test/mocks'; import configureStore from '../../../../redux/configureStore'; -import { - Detectors, - initialDetectorsState, -} from '../../../../redux/reducers/ad'; import { sampleHttpResponses } from '../../../Overview/utils/constants'; import { CoreServicesContext } from '../../../../components/CoreServices/CoreServices'; -const renderWithRouter = (isLoadingDetectors: boolean = false) => ({ +jest.mock('../../../../services', () => ({ + ...jest.requireActual('../../../../services'), + + getDataSourceEnabled: () => ({ + enabled: false + }) +})); + + +const renderWithRouter = () => ({ ...render( @@ -36,11 +42,11 @@ const renderWithRouter = (isLoadingDetectors: boolean = false) => ({ ( + render={(props: RouteComponentProps) => ( {' '} + setActionMenu={jest.fn()} + {...props}/> )} /> @@ -52,6 +58,17 @@ const renderWithRouter = (isLoadingDetectors: boolean = false) => ({ }); describe(' spec', () => { + beforeAll(() => { + Object.defineProperty(window, 'location', { + value: { + href: 'http://test.com', + pathname: '/', + search: '', + hash: '', + }, + writable: true + }); + }); jest.clearAllMocks(); describe('No sample detectors created', () => { test('renders component', async () => { 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 1cdefe53..0182660d 100644 --- a/public/pages/Overview/containers/__tests__/__snapshots__/AnomalyDetectionOverview.test.tsx.snap +++ b/public/pages/Overview/containers/__tests__/__snapshots__/AnomalyDetectionOverview.test.tsx.snap @@ -24,7 +24,7 @@ exports[` spec No sample detectors created renders c spec No sample detectors created renders c - `; @@ -785,7 +784,7 @@ exports[` spec Some detectors created renders compon spec Some detectors created renders compon - `; @@ -1541,7 +1539,7 @@ exports[` spec Some detectors created renders compon spec Some detectors created renders compon View detector and sample data @@ -2290,6 +2288,5 @@ exports[` spec Some detectors created renders compon - `; diff --git a/public/pages/ReviewAndCreate/containers/ReviewAndCreate.tsx b/public/pages/ReviewAndCreate/containers/ReviewAndCreate.tsx index 0f7335e7..81ca40de 100644 --- a/public/pages/ReviewAndCreate/containers/ReviewAndCreate.tsx +++ b/public/pages/ReviewAndCreate/containers/ReviewAndCreate.tsx @@ -34,7 +34,7 @@ import React, { Fragment, ReactElement, useEffect, useState } from 'react'; import { RouteComponentProps, useLocation } from 'react-router'; import { useDispatch, useSelector } from 'react-redux'; import { AppState } from '../../../redux/reducers'; -import { BREADCRUMBS, MAX_DETECTORS } from '../../../utils/constants'; +import { BREADCRUMBS, MAX_DETECTORS, MDS_BREADCRUMBS } from '../../../utils/constants'; import { useHideSideNavBar } from '../../main/hooks/useHideSideNavBar'; import { CoreStart, MountPoint } from '../../../../../../src/core/public'; import { CoreServicesContext } from '../../../components/CoreServices/CoreServices'; @@ -59,7 +59,7 @@ import { } from '../../../pages/utils/helpers'; import { getDataSourceManagementPlugin, - getDataSourcePlugin, + getDataSourceEnabled, getNotifications, getSavedObjectsClient, } from '../../../services'; @@ -77,7 +77,7 @@ export function ReviewAndCreate(props: ReviewAndCreateProps) { const location = useLocation(); const MDSQueryParams = getDataSourceFromURL(location); const dataSourceId = MDSQueryParams.dataSourceId; - const dataSourceEnabled = getDataSourcePlugin()?.dataSourceEnabled || false; + const dataSourceEnabled = getDataSourceEnabled().enabled; useHideSideNavBar(true, false); // This variable indicates if validate API declared detector settings as valid for AD creation @@ -177,11 +177,19 @@ export function ReviewAndCreate(props: ReviewAndCreateProps) { }, []); useEffect(() => { - core.chrome.setBreadcrumbs([ - BREADCRUMBS.ANOMALY_DETECTOR, - BREADCRUMBS.DETECTORS, - BREADCRUMBS.CREATE_DETECTOR, - ]); + if (dataSourceEnabled) { + core.chrome.setBreadcrumbs([ + MDS_BREADCRUMBS.ANOMALY_DETECTOR(dataSourceId), + MDS_BREADCRUMBS.DETECTORS(dataSourceId), + MDS_BREADCRUMBS.CREATE_DETECTOR, + ]); + } else { + core.chrome.setBreadcrumbs([ + BREADCRUMBS.ANOMALY_DETECTOR, + BREADCRUMBS.DETECTORS, + BREADCRUMBS.CREATE_DETECTOR, + ]); + } }, []); const handleSubmit = async ( diff --git a/public/pages/ReviewAndCreate/containers/__tests__/ReviewAndCreate.test.tsx b/public/pages/ReviewAndCreate/containers/__tests__/ReviewAndCreate.test.tsx index f3209fff..35cac0f0 100644 --- a/public/pages/ReviewAndCreate/containers/__tests__/ReviewAndCreate.test.tsx +++ b/public/pages/ReviewAndCreate/containers/__tests__/ReviewAndCreate.test.tsx @@ -27,6 +27,14 @@ import { INITIAL_DETECTOR_JOB_VALUES } from '../../../DetectorJobs/utils/constan import { INITIAL_MODEL_CONFIGURATION_VALUES } from '../../../ConfigureModel/utils/constants'; import { INITIAL_DETECTOR_DEFINITION_VALUES } from '../../../DefineDetector/utils/constants'; +jest.mock('../../../../services', () => ({ + ...jest.requireActual('../../../../services'), + + getDataSourceEnabled: () => ({ + enabled: false + }) +})); + const renderWithRouter = (isEdit: boolean = false) => ({ ...render( diff --git a/public/pages/utils/helpers.ts b/public/pages/utils/helpers.ts index 321f6fc4..2467f7dc 100644 --- a/public/pages/utils/helpers.ts +++ b/public/pages/utils/helpers.ts @@ -20,7 +20,7 @@ import { DETECTORS_QUERY_PARAMS, SORT_DIRECTION } from '../../../server/utils/co import { ALL_INDICES, ALL_DETECTOR_STATES, MAX_DETECTORS, DEFAULT_QUERY_PARAMS } from './constants'; import { DETECTOR_STATE } from '../../../server/utils/constants'; import { timeFormatter } from '@elastic/charts'; -import { getDataSourcePlugin } from '../../services'; +import { getDataSourceEnabled, getDataSourcePlugin } from '../../services'; export function sanitizeSearchText(searchValue: string): string { if (!searchValue || searchValue == '*') { @@ -155,7 +155,7 @@ export const constructHrefWithDataSourceId = ( dataSourceId: string = '', withHash: Boolean ): string => { - const dataSourceEnabled = getDataSourcePlugin()?.dataSourceEnabled || false; + const dataSourceEnabled = getDataSourceEnabled().enabled; const url = new URLSearchParams(); // Set up base parameters for '/detectors' diff --git a/public/plugin.ts b/public/plugin.ts index 07eb6194..ed771c26 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -37,7 +37,7 @@ import { setQueryService, setSavedObjectsClient, setDataSourceManagementPlugin, - setDataSourcePlugin, + setDataSourceEnabled, } from './services'; import { AnomalyDetectionOpenSearchDashboardsPluginStart } from 'public'; import { @@ -100,7 +100,9 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin setDataSourceManagementPlugin(plugins.dataSourceManagement); - setDataSourcePlugin(plugins.dataSource); + const enabled = !!plugins.dataSource; + + setDataSourceEnabled({ enabled }); // Create context menu actions const actions = getActions(); diff --git a/public/redux/reducers/__tests__/anomalyResults.test.ts b/public/redux/reducers/__tests__/anomalyResults.test.ts index bc873208..e668e2d1 100644 --- a/public/redux/reducers/__tests__/anomalyResults.test.ts +++ b/public/redux/reducers/__tests__/anomalyResults.test.ts @@ -20,6 +20,14 @@ import reducer, { initialDetectorsState, } from '../anomalyResults'; +jest.mock('../../../services', () => ({ + ...jest.requireActual('../../../services'), + + getDataSourceEnabled: () => ({ + enabled: false + }) +})); + describe('anomaly results reducer actions', () => { let store: MockStore; beforeEach(() => { @@ -45,6 +53,7 @@ describe('anomaly results reducer actions', () => { await store.dispatch( getDetectorResults( tempDetectorId, + '', queryParams, false, resultIndex, @@ -87,7 +96,7 @@ describe('anomaly results reducer actions', () => { }; try { await store.dispatch( - getDetectorResults(tempDetectorId, queryParams, false, '', false) + getDetectorResults(tempDetectorId, '', queryParams, false, '', false) ); } catch (e) { const actions = store.getActions(); diff --git a/public/services.ts b/public/services.ts index 4cb502f1..dbe060e6 100644 --- a/public/services.ts +++ b/public/services.ts @@ -17,6 +17,10 @@ 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 interface DataSourceEnabled { + enabled: boolean; +} + export const [getSavedFeatureAnywhereLoader, setSavedFeatureAnywhereLoader] = createGetterSetter('savedFeatureAnywhereLoader'); @@ -47,8 +51,8 @@ export const [getSavedObjectsClient, setSavedObjectsClient] = export const [getDataSourceManagementPlugin, setDataSourceManagementPlugin] = createGetterSetter('DataSourceManagement'); -export const [getDataSourcePlugin, setDataSourcePlugin] = - createGetterSetter('DataSource'); +export const [getDataSourceEnabled, setDataSourceEnabled] = + createGetterSetter('DataSourceEnabled'); // This is primarily used for mocking this module and each of its fns in tests. export default { diff --git a/public/utils/constants.ts b/public/utils/constants.ts index ac7907a7..fa3022d6 100644 --- a/public/utils/constants.ts +++ b/public/utils/constants.ts @@ -26,6 +26,15 @@ export const BREADCRUMBS = Object.freeze({ EDIT_MODEL_CONFIGURATION: { text: 'Edit model configuration' }, }); +export const MDS_BREADCRUMBS = Object.freeze({ + ANOMALY_DETECTOR: (dataSourceId?: string) => ({ text: 'Anomaly detection', href: `#/?dataSourceId=${dataSourceId}` }), + DETECTORS: (dataSourceId?: string) => ({ text: 'Detectors', href: `#/detectors?dataSourceId=${dataSourceId}` }), + CREATE_DETECTOR: { text: 'Create detector' }, + EDIT_DETECTOR: { text: 'Edit detector' }, + DASHBOARD: (dataSourceId?: string) => ({ text: 'Dashboard', href: `#/dashboard?dataSourceId=${dataSourceId}` }), + EDIT_MODEL_CONFIGURATION: { text: 'Edit model configuration' }, +}); + export const APP_PATH = { DASHBOARD: '/dashboard', LIST_DETECTORS: '/detectors', diff --git a/server/plugin.ts b/server/plugin.ts index 2c925ffd..a6dfc3b0 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -65,19 +65,19 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin const globalConfig = await this.globalConfig$.pipe(first()).toPromise(); const { customHeaders, ...rest } = globalConfig.opensearch; - 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) { - client = core.opensearch.legacy.createClient('anomaly_detection', { + // 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, - }); - } else { + } + ); + + const dataSourceEnabled = !!dataSource; + + if (dataSourceEnabled) { dataSource.registerCustomApiSchema(adPlugin); dataSource.registerCustomApiSchema(alertingPlugin); } diff --git a/server/routes/ad.ts b/server/routes/ad.ts index 1acbcc18..ed42e7e9 100644 --- a/server/routes/ad.ts +++ b/server/routes/ad.ts @@ -74,7 +74,7 @@ export function registerADRoutes(apiRouter: Router, adService: AdService) { apiRouter.post('/detectors/_search', adService.searchDetector); // post search anomaly results - apiRouter.post('/detectors/results/_search/', adService.searchResults); + apiRouter.post('/detectors/results/_search', adService.searchResults); apiRouter.post( '/detectors/results/_search/{dataSourceId}', adService.searchResults diff --git a/server/utils/helpers.ts b/server/utils/helpers.ts index 4795e701..c9006544 100644 --- a/server/utils/helpers.ts +++ b/server/utils/helpers.ts @@ -93,7 +93,7 @@ export function getClientBasedOnDataSource( dataSourceEnabled: boolean, request: OpenSearchDashboardsRequest, dataSourceId: string, - client: ILegacyClusterClient | undefined + client: ILegacyClusterClient ): ( endpoint: string, clientParams?: Record, @@ -103,9 +103,6 @@ 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; }