Skip to content

Commit

Permalink
[ML] Kibana management jobs list (#42570)
Browse files Browse the repository at this point in the history
* Add Ml link section to management page

* Use job management table in kibana management

* add tabs and breadcrumbs

* link to filtered job manangement table

* Reduce expanded row tabs shown in management table

* add KM job list section styles

* Only anomaly detection jobs tab for now

* wip: adds access denied page

* fix types

* update mlManagement permission check to check license

* update permissions check for manage_ml

* Update permission check to manage_ml

* update privileges key type

* if spaces is disabled or ignoreSpace is true force isMlEnabledInSpace to be true

* remove id from filter on clear
  • Loading branch information
alvarezmelissa87 committed Aug 9, 2019
1 parent a209f1f commit acabe3d
Show file tree
Hide file tree
Showing 27 changed files with 690 additions and 73 deletions.
1 change: 1 addition & 0 deletions x-pack/legacy/plugins/ml/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const ml = (kibana: any) => {
publicDir: resolve(__dirname, 'public'),

uiExports: {
managementSections: ['plugins/ml/management'],
app: {
title: i18n.translate('xpack.ml.mlNavTitle', {
defaultMessage: 'Machine Learning',
Expand Down
5 changes: 5 additions & 0 deletions x-pack/legacy/plugins/ml/public/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
// ML has it's own variables for coloring
@import 'variables';

// Kibana management page ML section
#kibanaManagementMLSection {
@import 'management/index';
}

// Protect the rest of Kibana from ML generic namespacing
// SASSTODO: Prefix ml selectors instead
#ml-app {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class JobDetailsUI extends Component {
datafeedTimingStats
} = extractJobDetails(job);

const { intl } = this.props;
const { intl, showFullDetails } = this.props;

const tabs = [{
id: 'job-settings',
Expand All @@ -88,13 +88,6 @@ class JobDetailsUI extends Component {
defaultMessage: 'Job config'
}),
content: <JobDetailsPane sections={[detectors, influencers, analysisConfig, analysisLimits, dataDescription]} />,
}, {
id: 'datafeed',
name: intl.formatMessage({
id: 'xpack.ml.jobsList.jobDetails.tabs.datafeedLabel',
defaultMessage: 'Datafeed'
}),
content: <JobDetailsPane sections={[datafeed, datafeedTimingStats]} />,
}, {
id: 'counts',
name: intl.formatMessage({
Expand All @@ -116,24 +109,38 @@ class JobDetailsUI extends Component {
defaultMessage: 'Job messages'
}),
content: <JobMessagesPane job={job} />,
}, {
id: 'datafeed-preview',
name: intl.formatMessage({
id: 'xpack.ml.jobsList.jobDetails.tabs.datafeedPreviewLabel',
defaultMessage: 'Datafeed preview'
}),
content: <DatafeedPreviewPane job={job} />,
}, {
id: 'forecasts',
name: intl.formatMessage({
id: 'xpack.ml.jobsList.jobDetails.tabs.forecastsLabel',
defaultMessage: 'Forecasts'
}),
content: <ForecastsTable job={job} />,
}
},
];

if (mlAnnotationsEnabled) {
if (showFullDetails) {
// Datafeed should be at index 2 in tabs array for full details
tabs.splice(2, 0, {
id: 'datafeed',
name: intl.formatMessage({
id: 'xpack.ml.jobsList.jobDetails.tabs.datafeedLabel',
defaultMessage: 'Datafeed'
}),
content: <JobDetailsPane sections={[datafeed, datafeedTimingStats]} />,
});

tabs.push({
id: 'datafeed-preview',
name: intl.formatMessage({
id: 'xpack.ml.jobsList.jobDetails.tabs.datafeedPreviewLabel',
defaultMessage: 'Datafeed preview'
}),
content: <DatafeedPreviewPane job={job} />,
}, {
id: 'forecasts',
name: intl.formatMessage({
id: 'xpack.ml.jobsList.jobDetails.tabs.forecastsLabel',
defaultMessage: 'Forecasts'
}),
content: <ForecastsTable job={job} />,
});
}

if (mlAnnotationsEnabled && showFullDetails) {
tabs.push({
id: 'annotations',
name: intl.formatMessage({
Expand Down Expand Up @@ -166,6 +173,7 @@ JobDetailsUI.propTypes = {
job: PropTypes.object,
addYourself: PropTypes.func.isRequired,
removeYourself: PropTypes.func.isRequired,
showFullDetails: PropTypes.bool
};

export const JobDetails = injectI18n(JobDetailsUI);
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import React, {

import { ml } from 'plugins/ml/services/ml_api_service';
import { JobGroup } from '../job_group';
import { getSelectedJobIdFromUrl, clearSelectedJobIdFromUrl } from '../utils';

import {
EuiSearchBar,
Expand Down Expand Up @@ -56,10 +57,30 @@ class JobFilterBarUI extends Component {
this.setFilters = props.setFilters;
}

urlFilterIdCleared = false;

componentDidMount() {
// If job id is selected in url, filter table to that id
const selectedId = getSelectedJobIdFromUrl(window.location.href);
if (selectedId !== undefined) {
this.setState({
selectedId
}, () => {
// trigger onChange with query for job id to trigger table filter
const query = EuiSearchBar.Query.parse(selectedId);
this.onChange({ query });
});
}
}

onChange = ({ query, error }) => {
if (error) {
this.setState({ error });
} else {
if (query.text === '' && this.urlFilterIdCleared === false) {
this.urlFilterIdCleared = true;
clearSelectedJobIdFromUrl(window.location.href);
}
let clauses = [];
if (query && query.ast !== undefined && query.ast.clauses !== undefined) {
clauses = query.ast.clauses;
Expand All @@ -71,7 +92,7 @@ class JobFilterBarUI extends Component {

render() {
const { intl } = this.props;
const { error } = this.state;
const { error, selectedId } = this.state;
const filters = [
{
type: 'field_value_toggle_group',
Expand Down Expand Up @@ -133,10 +154,12 @@ class JobFilterBarUI extends Component {
}

];

// if prop flag for default filter set to true
// set defaultQuery to job id and force trigger filter with onChange - pass it the query object for the job id
return (
<EuiFlexGroup direction="column">
<EuiFlexItem data-test-subj="mlJobListSearchBar" grow={false}>
{selectedId === undefined &&
<EuiSearchBar
box={{
incremental: true,
Expand All @@ -145,6 +168,17 @@ class JobFilterBarUI extends Component {
onChange={this.onChange}
className="mlJobFilterBar"
/>
}
{selectedId !== undefined &&
<EuiSearchBar
box={{
incremental: true,
}}
defaultQuery={selectedId}
filters={filters}
onChange={this.onChange}
className="mlJobFilterBar"
/>}
<EuiFormRow
fullWidth
isInvalid={(error !== null)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,22 @@ import { tabColor } from '../../../../../common/util/group_color_utils';

import PropTypes from 'prop-types';
import React from 'react';
import theme from '@elastic/eui/dist/eui_theme_light.json';


export function JobGroup({ name }) {
return (
<div
className="inline-group"
style={{ backgroundColor: tabColor(name) }}
style={{
backgroundColor: tabColor(name),
display: 'inline-block',
padding: '2px 5px',
borderRadius: '2px',
fontSize: '12px',
margin: '0px 3px',
color: theme.euiColorEmptyShade
}}
>
{name}
</div>
Expand Down
Loading

0 comments on commit acabe3d

Please sign in to comment.