Skip to content

Commit

Permalink
[ML] Adds DF Transform Analytics list to Kibana management (#43151)
Browse files Browse the repository at this point in the history
* disable job link if ml not enabled in space

* add analytics table in managment page

* update css for analytics table

* only show ML section if license if trial/platinum

* remove actions column for analytics list in KM

* fix typescript error on columns

* update props interface for analytics table

* plain text instead of disabled link if not enabled in space
  • Loading branch information
alvarezmelissa87 committed Aug 14, 2019
1 parent 6be8b32 commit 7aa7f5a
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ function stringMatch(str: string | undefined, substr: string) {
);
}

export const DataFrameAnalyticsList: FC = () => {
interface Props {
isManagementTable?: boolean;
}
// isManagementTable - for use in Kibana managagement ML section
export const DataFrameAnalyticsList: FC<Props> = ({ isManagementTable }) => {
const [isInitialized, setIsInitialized] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [blockRefresh, setBlockRefresh] = useState(false);
Expand Down Expand Up @@ -225,7 +229,7 @@ export const DataFrameAnalyticsList: FC = () => {
);
}

const columns = getColumns(expandedRowItemIds, setExpandedRowItemIds);
const columns = getColumns(expandedRowItemIds, setExpandedRowItemIds, isManagementTable);

const sorting = {
sort: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ export const getTaskStateBadge = (

export const getColumns = (
expandedRowItemIds: DataFrameAnalyticsId[],
setExpandedRowItemIds: React.Dispatch<React.SetStateAction<DataFrameAnalyticsId[]>>
setExpandedRowItemIds: React.Dispatch<React.SetStateAction<DataFrameAnalyticsId[]>>,
isManagementTable: boolean = false
) => {
const actions = getActions();

Expand All @@ -75,8 +76,8 @@ export const getColumns = (
// spread to a new array otherwise the component wouldn't re-render
setExpandedRowItemIds([...expandedRowItemIds]);
}

return [
// update possible column types to something like (FieldDataColumn | ComputedColumn | ActionsColumn)[] when they have been added to EUI
const columns: any[] = [
{
align: RIGHT_ALIGNMENT,
width: '40px',
Expand Down Expand Up @@ -205,12 +206,17 @@ export const getColumns = (
},
width: '100px',
},
{
];

if (isManagementTable === false) {
columns.push({
name: i18n.translate('xpack.ml.dataframe.analyticsList.tableActionLabel', {
defaultMessage: 'Actions',
}),
actions,
width: '200px',
},
];
});
}

return columns;
};
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ class JobsListUI extends Component {
};

getJobIdLink(id) {
// Don't allow link to job if ML is not enabled in current space
if (this.props.isMlEnabledInSpace === false) {
return id;
}

return (
<EuiLink href={getJobIdUrl(id)}>
{id}
Expand Down Expand Up @@ -253,6 +258,10 @@ class JobsListUI extends Component {
<EuiBadge color={'hollow'}>{'all'}</EuiBadge>
)
});
// Remove actions if Ml not enabled in current space
if (this.props.isMlEnabledInSpace === false) {
columns.pop();
}
} else {
// insert before last column
columns.splice(columns.length - 1, 0, {
Expand Down Expand Up @@ -344,6 +353,8 @@ class JobsListUI extends Component {
JobsListUI.propTypes = {
jobsSummaryList: PropTypes.array.isRequired,
fullJobsList: PropTypes.object.isRequired,
isManagementTable: PropTypes.bool,
isMlEnabledInSpace: PropTypes.bool,
itemIdToExpandedRowMap: PropTypes.object.isRequired,
toggleRow: PropTypes.func.isRequired,
selectJobChange: PropTypes.func.isRequired,
Expand All @@ -355,6 +366,8 @@ JobsListUI.propTypes = {
loading: PropTypes.bool,
};
JobsListUI.defaultProps = {
isManagementTable: false,
isMlEnabledInSpace: true,
loading: false,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,21 +362,22 @@ export class JobsListView extends Component {
}

renderManagementJobsListComponents() {
const { loading } = this.state;
const { loading, itemIdToExpandedRowMap, filteredJobsSummaryList, fullJobsList, selectedJobs } = this.state;
return (
<div className="managementJobsList">
<div>
<JobFilterBar setFilters={this.setFilters} />
</div>
<JobsList
jobsSummaryList={this.state.filteredJobsSummaryList}
fullJobsList={this.state.fullJobsList}
itemIdToExpandedRowMap={this.state.itemIdToExpandedRowMap}
jobsSummaryList={filteredJobsSummaryList}
fullJobsList={fullJobsList}
itemIdToExpandedRowMap={itemIdToExpandedRowMap}
toggleRow={this.toggleRow}
selectJobChange={this.selectJobChange}
selectedJobsCount={this.state.selectedJobs.length}
selectedJobsCount={selectedJobs.length}
loading={loading}
isManagementTable={true}
isMlEnabledInSpace={this.props.isMlEnabledInSpace}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { each } from 'lodash';
import { toastNotifications } from 'ui/notify';
import { mlMessageBarService } from 'plugins/ml/components/messagebar/messagebar_service';
import rison from 'rison-node';
import chrome from 'ui/chrome'; // TODO: get from context once walter's PR is merged
import chrome from 'ui/chrome';

import { mlJobService } from 'plugins/ml/services/job_service';
import { ml } from 'plugins/ml/services/ml_api_service';
Expand Down
6 changes: 5 additions & 1 deletion x-pack/legacy/plugins/ml/public/management/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ import { management } from 'ui/management';
import { xpackInfo } from 'plugins/xpack_main/services/xpack_info';
import { i18n } from '@kbn/i18n';
import { JOBS_LIST_PATH } from './management_urls';
import { LICENSE_TYPE } from '../../common/constants/license';
import 'plugins/ml/management/jobs_list';

if (xpackInfo.get('features.ml.showLinks', false) === true) {
if (
xpackInfo.get('features.ml.showLinks', false) === true &&
xpackInfo.get('features.ml.licenseType') === LICENSE_TYPE.FULL
) {
management.register('ml', {
display: i18n.translate('xpack.ml.management.mlTitle', {
defaultMessage: 'Machine Learning',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@import './jobs_list_page/stats_bar';
@import './jobs_list_page/buttons';
@import './jobs_list_page/expanded_row';
@import './jobs_list_page/analytics_table';
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

.mlAnalyticsTable {
// Using an override as a last resort because we cannot set custom classes on
// nested upstream components. The opening animation limits the height
// of the expanded row to 1000px which turned out to be not predictable.
// The animation could also result in flickering with expanded rows
// where the inner content would result in the DOM changing the height.
.euiTableRow-isExpandedRow .euiTableCellContent {
animation: none !important;
.euiTableCellContent__text {
width: 100%;
}
}
// Another override: Because an update to the table replaces the DOM, the same
// icon would still again fade in with an animation. If the table refreshes with
// e.g. 1s this would result in a blinking icon effect.
.euiIcon-isLoaded {
animation: none !important;
}
}

.mlAnalyticsProgressBar {
margin-bottom: $euiSizeM;
}

.mlTaskStateBadge {
max-width: 100px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { Fragment } from 'react';
import React, { Fragment, FC } from 'react';
import { i18n } from '@kbn/i18n';
import { I18nContext } from 'ui/i18n';
import {
Expand All @@ -19,8 +19,13 @@ import {

// @ts-ignore undeclared module
import { JobsListView } from '../../../../jobs/jobs_list/components/jobs_list_view';
import { DataFrameAnalyticsList } from '../../../../data_frame_analytics/pages/analytics_management/components/analytics_list';

export const JobsListPage = () => {
interface Props {
isMlEnabledInSpace: boolean;
}

export const JobsListPage: FC<Props> = ({ isMlEnabledInSpace }) => {
const tabs = [
{
id: 'anomaly_detection_jobs',
Expand All @@ -30,23 +35,24 @@ export const JobsListPage = () => {
content: (
<Fragment>
<EuiSpacer size="m" />
<JobsListView isManagementTable={true} />
<JobsListView isManagementTable={true} isMlEnabledInSpace={isMlEnabledInSpace} />
</Fragment>
),
},
{
id: 'analytics_jobs',
name: i18n.translate('xpack.ml.management.jobsList.analyticsTab', {
defaultMessage: 'Analytics',
}),
content: (
<Fragment>
<EuiSpacer size="m" />
<DataFrameAnalyticsList isManagementTable={true} />
</Fragment>
),
},
// {
// id: 'analytics_jobs',
// name: i18n.translate('xpack.ml.management.jobsList.analyticsTab', {
// defaultMessage: 'Analytics',
// }),
// content: renderAnalyticsJobs(),
// },
];

// function renderAnalyticsJobs() {
// return <div>Analytics job placeholder</div>;
// }

function renderTabs() {
return <EuiTabbedContent size="s" tabs={tabs} initialSelectedTab={tabs[0]} />;
}
Expand Down
13 changes: 9 additions & 4 deletions x-pack/legacy/plugins/ml/public/management/jobs_list/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/

// @ts-ignore no declaration module
import { ReactDOM, render, unmountComponentAtNode } from 'react-dom';
import ReactDOM, { render, unmountComponentAtNode } from 'react-dom';
import React from 'react';
import routes from 'ui/routes';
import { canGetManagementMlJobs } from '../../privilege/check_privilege';
import { JOBS_LIST_PATH, ACCESS_DENIED_PATH } from '../management_urls';
Expand All @@ -22,14 +22,19 @@ routes.when(JOBS_LIST_PATH, {
resolve: {
checkPrivilege: canGetManagementMlJobs,
},
controller($scope) {
controller($scope, checkPrivilege) {
const { mlFeatureEnabledInSpace } = checkPrivilege;

$scope.$on('$destroy', () => {
const elem = document.getElementById('kibanaManagementMLSection');
if (elem) unmountComponentAtNode(elem);
});
$scope.$$postDigest(() => {
const element = document.getElementById('kibanaManagementMLSection');
render(JobsListPage(), element);
ReactDOM.render(
React.createElement(JobsListPage, { isMlEnabledInSpace: mlFeatureEnabledInSpace }),
element
);
});
},
});
Expand Down
22 changes: 12 additions & 10 deletions x-pack/legacy/plugins/ml/public/privilege/check_privilege.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@ let privileges: Privileges = getDefaultPrivileges();
// manage_ml requires all monitor and admin cluster privileges: https://github.com/elastic/elasticsearch/blob/664a29c8905d8ce9ba8c18aa1ed5c5de93a0eabc/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilege.java#L53
export function canGetManagementMlJobs(kbnUrl: any) {
return new Promise((resolve, reject) => {
getManageMlPrivileges().then(({ capabilities, isPlatinumOrTrialLicense }) => {
privileges = capabilities;
// Loop through all privilages to ensure they are all set to true.
const isManageML = Object.values(privileges).every(p => p === true);
getManageMlPrivileges().then(
({ capabilities, isPlatinumOrTrialLicense, mlFeatureEnabledInSpace }) => {
privileges = capabilities;
// Loop through all privilages to ensure they are all set to true.
const isManageML = Object.values(privileges).every(p => p === true);

if (isManageML === true && isPlatinumOrTrialLicense === true) {
return resolve();
} else {
kbnUrl.redirect(ACCESS_DENIED_PATH);
return reject();
if (isManageML === true && isPlatinumOrTrialLicense === true) {
return resolve({ mlFeatureEnabledInSpace });
} else {
kbnUrl.redirect(ACCESS_DENIED_PATH);
return reject();
}
}
});
);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ interface Response {
export function privilegesProvider(
callWithRequest: callWithRequestType,
xpackMainPlugin: XPackMainPlugin,
isMlEnabledInSpace: () => Promise<boolean>
isMlEnabledInSpace: () => Promise<boolean>,
ignoreSpaces: boolean = false
) {
const { isUpgradeInProgress } = upgradeCheckProvider(callWithRequest);
async function getPrivileges(): Promise<Response> {
Expand All @@ -47,7 +48,7 @@ export function privilegesProvider(
? setFullActionPrivileges
: setBasicActionPrivileges;

if (mlFeatureEnabledInSpace === false) {
if (mlFeatureEnabledInSpace === false && ignoreSpaces === false) {
// if ML isn't enabled in the current space,
// return with the default privileges (all false)
return {
Expand Down
6 changes: 3 additions & 3 deletions x-pack/legacy/plugins/ml/server/routes/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,12 @@ export function systemRoutes({
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
try {
const ignoreSpaces = request.query && request.query.ignoreSpaces === 'true';
// if spaces is disabled or ignoreSpace is true force isMlEnabledInSpace to be true
const { isMlEnabledInSpace } = (spacesPlugin !== undefined && ignoreSpaces === false) ?
// if spaces is disabled force isMlEnabledInSpace to be true
const { isMlEnabledInSpace } = spacesPlugin !== undefined ?
spacesUtilsProvider(spacesPlugin, request, config) :
{ isMlEnabledInSpace: async () => true };

const { getPrivileges } = privilegesProvider(callWithRequest, xpackMainPlugin, isMlEnabledInSpace);
const { getPrivileges } = privilegesProvider(callWithRequest, xpackMainPlugin, isMlEnabledInSpace, ignoreSpaces);
return await getPrivileges();
} catch (error) {
return wrapError(error);
Expand Down

0 comments on commit 7aa7f5a

Please sign in to comment.