Skip to content

Commit

Permalink
[ML] Fix analytics list on management page. (#74254)
Browse files Browse the repository at this point in the history
The analytics job page in the Kibana management section didn't have the context provided by React Router and Kibana's own history object so the page crashed on load. The context is necessary to construct the correct URLs to navigate to the ML plugin itself.
This PR fixes it by wrapping the management page in <Router history={history} />. Also adds functional tests to cover navigating to the jobs list pages in stack management.
  • Loading branch information
walterra authored Aug 6, 2020
1 parent 3064c6e commit 11c74bc
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import React, { useEffect, useState, Fragment, FC } from 'react';
import { Router } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
import { CoreStart } from 'kibana/public';

Expand All @@ -20,6 +21,8 @@ import {
EuiTitle,
} from '@elastic/eui';

import { ManagementAppMountParams } from '../../../../../../../../../src/plugins/management/public/';

import { checkGetManagementMlJobsResolver } from '../../../../capabilities/check_capabilities';
import { KibanaContextProvider } from '../../../../../../../../../src/plugins/kibana_react/public';

Expand All @@ -30,6 +33,7 @@ import { DataFrameAnalyticsList } from '../../../../data_frame_analytics/pages/a
import { AccessDeniedPage } from '../access_denied_page';

interface Tab {
'data-test-subj': string;
id: string;
name: string;
content: any;
Expand All @@ -38,6 +42,7 @@ interface Tab {
function getTabs(isMlEnabledInSpace: boolean): Tab[] {
return [
{
'data-test-subj': 'mlStackManagementJobsListAnomalyDetectionTab',
id: 'anomaly_detection_jobs',
name: i18n.translate('xpack.ml.management.jobsList.anomalyDetectionTab', {
defaultMessage: 'Anomaly detection',
Expand All @@ -50,6 +55,7 @@ function getTabs(isMlEnabledInSpace: boolean): Tab[] {
),
},
{
'data-test-subj': 'mlStackManagementJobsListAnalyticsTab',
id: 'analytics_jobs',
name: i18n.translate('xpack.ml.management.jobsList.analyticsTab', {
defaultMessage: 'Analytics',
Expand All @@ -67,7 +73,10 @@ function getTabs(isMlEnabledInSpace: boolean): Tab[] {
];
}

export const JobsListPage: FC<{ coreStart: CoreStart }> = ({ coreStart }) => {
export const JobsListPage: FC<{
coreStart: CoreStart;
history: ManagementAppMountParams['history'];
}> = ({ coreStart, history }) => {
const [initialized, setInitialized] = useState(false);
const [accessDenied, setAccessDenied] = useState(false);
const [isMlEnabledInSpace, setIsMlEnabledInSpace] = useState(false);
Expand Down Expand Up @@ -128,46 +137,51 @@ export const JobsListPage: FC<{ coreStart: CoreStart }> = ({ coreStart }) => {
return (
<I18nContext>
<KibanaContextProvider services={{ ...coreStart }}>
<EuiPageContent id="kibanaManagementMLSection">
<EuiTitle size="l">
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<h1>
{i18n.translate('xpack.ml.management.jobsList.jobsListTitle', {
defaultMessage: 'Machine Learning Jobs',
})}
</h1>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
target="_blank"
iconType="help"
iconSide="left"
color="primary"
href={
currentTabId === 'anomaly_detection_jobs'
? anomalyDetectionJobsUrl
: anomalyJobsUrl
}
>
{currentTabId === 'anomaly_detection_jobs'
? anomalyDetectionDocsLabel
: analyticsDocsLabel}
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
</EuiTitle>
<EuiSpacer size="s" />
<EuiTitle size="s">
<EuiText color="subdued">
{i18n.translate('xpack.ml.management.jobsList.jobsListTagline', {
defaultMessage: 'View machine learning analytics and anomaly detection jobs.',
})}
</EuiText>
</EuiTitle>
<EuiSpacer size="l" />
<EuiPageContentBody>{renderTabs()}</EuiPageContentBody>
</EuiPageContent>
<Router history={history}>
<EuiPageContent
id="kibanaManagementMLSection"
data-test-subj="mlPageStackManagementJobsList"
>
<EuiTitle size="l">
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<h1>
{i18n.translate('xpack.ml.management.jobsList.jobsListTitle', {
defaultMessage: 'Machine Learning Jobs',
})}
</h1>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
target="_blank"
iconType="help"
iconSide="left"
color="primary"
href={
currentTabId === 'anomaly_detection_jobs'
? anomalyDetectionJobsUrl
: anomalyJobsUrl
}
>
{currentTabId === 'anomaly_detection_jobs'
? anomalyDetectionDocsLabel
: analyticsDocsLabel}
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
</EuiTitle>
<EuiSpacer size="s" />
<EuiTitle size="s">
<EuiText color="subdued">
{i18n.translate('xpack.ml.management.jobsList.jobsListTagline', {
defaultMessage: 'View machine learning analytics and anomaly detection jobs.',
})}
</EuiText>
</EuiTitle>
<EuiSpacer size="l" />
<EuiPageContentBody>{renderTabs()}</EuiPageContentBody>
</EuiPageContent>
</Router>
</KibanaContextProvider>
</I18nContext>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ import { getJobsListBreadcrumbs } from '../breadcrumbs';
import { setDependencyCache, clearCache } from '../../util/dependency_cache';
import './_index.scss';

const renderApp = (element: HTMLElement, coreStart: CoreStart) => {
ReactDOM.render(React.createElement(JobsListPage, { coreStart }), element);
const renderApp = (
element: HTMLElement,
history: ManagementAppMountParams['history'],
coreStart: CoreStart
) => {
ReactDOM.render(React.createElement(JobsListPage, { coreStart, history }), element);
return () => {
unmountComponentAtNode(element);
clearCache();
Expand All @@ -37,5 +41,5 @@ export async function mountApp(

params.setBreadcrumbs(getJobsListBreadcrumbs());

return renderApp(params.element, coreStart);
return renderApp(params.element, params.history, coreStart);
}
12 changes: 12 additions & 0 deletions x-pack/test/functional/apps/ml/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,17 @@ export default function ({ getService }: FtrProviderContext) {
await ml.dataVisualizer.assertDataVisualizerImportDataCardExists();
await ml.dataVisualizer.assertDataVisualizerIndexDataCardExists();
});

it('should load the stack management with the ML menu item being present', async () => {
await ml.navigation.navigateToStackManagement();
});

it('should load the jobs list page in stack management', async () => {
await ml.navigation.navigateToStackManagementJobsListPage();
});

it('should load the analytics jobs list page in stack management', async () => {
await ml.navigation.navigateToStackManagementJobsListPageAnalyticsTab();
});
});
}
27 changes: 27 additions & 0 deletions x-pack/test/functional/services/ml/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ export function MachineLearningNavigationProvider({
});
},

async navigateToStackManagement() {
await retry.tryForTime(60 * 1000, async () => {
await PageObjects.common.navigateToApp('management');
await testSubjects.existOrFail('jobsListLink', { timeout: 2000 });
});
},

async assertTabsExist(tabTypeSubject: string, areaSubjects: string[]) {
await retry.tryForTime(10000, async () => {
const allTabs = await testSubjects.findAll(`~${tabTypeSubject}`, 3);
Expand Down Expand Up @@ -76,5 +83,25 @@ export function MachineLearningNavigationProvider({
async navigateToSettings() {
await this.navigateToArea('~mlMainTab & ~settings', 'mlPageSettings');
},

async navigateToStackManagementJobsListPage() {
// clicks the jobsListLink and loads the jobs list page
await testSubjects.click('jobsListLink');
await retry.tryForTime(60 * 1000, async () => {
// verify that the overall page is present
await testSubjects.existOrFail('mlPageStackManagementJobsList');
// verify that the default tab with the anomaly detection jobs list got loaded
await testSubjects.existOrFail('ml-jobs-list');
});
},

async navigateToStackManagementJobsListPageAnalyticsTab() {
// clicks the `Analytics` tab and loads the analytics list page
await testSubjects.click('mlStackManagementJobsListAnalyticsTab');
await retry.tryForTime(60 * 1000, async () => {
// verify that the empty prompt for analytics jobs list got loaded
await testSubjects.existOrFail('mlNoDataFrameAnalyticsFound');
});
},
};
}

0 comments on commit 11c74bc

Please sign in to comment.