From 9903dd54819b434a9f6cf17f3fe96a6e427973c7 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Tue, 10 Mar 2020 15:59:51 +0100 Subject: [PATCH 1/3] [ML] Register NP ML plugin for Kibana management section. --- .../plugins/ml/public/application/app.tsx | 2 + .../ml/public/application/management/index.ts | 30 ++++----- .../application/management/jobs_list/index.ts | 63 +++++-------------- x-pack/legacy/plugins/ml/public/plugin.ts | 7 ++- 4 files changed, 39 insertions(+), 63 deletions(-) diff --git a/x-pack/legacy/plugins/ml/public/application/app.tsx b/x-pack/legacy/plugins/ml/public/application/app.tsx index 18545f31f03c7..a8ad93670cdb7 100644 --- a/x-pack/legacy/plugins/ml/public/application/app.tsx +++ b/x-pack/legacy/plugins/ml/public/application/app.tsx @@ -12,6 +12,7 @@ import 'ace'; import { AppMountParameters, CoreStart } from 'kibana/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; +import { ManagementSetup } from 'src/plugins/management/public'; import { SecurityPluginSetup } from '../../../../../plugins/security/public'; import { LicensingPluginSetup } from '../../../../../plugins/licensing/public'; @@ -25,6 +26,7 @@ export interface MlDependencies extends AppMountParameters { data: DataPublicPluginStart; security: SecurityPluginSetup; licensing: LicensingPluginSetup; + management: ManagementSetup; } interface AppProps { diff --git a/x-pack/legacy/plugins/ml/public/application/management/index.ts b/x-pack/legacy/plugins/ml/public/application/management/index.ts index 99a2e8353a874..9f2d489d6224c 100644 --- a/x-pack/legacy/plugins/ml/public/application/management/index.ts +++ b/x-pack/legacy/plugins/ml/public/application/management/index.ts @@ -10,19 +10,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import { npSetup } from 'ui/new_platform'; -import { management } from 'ui/management'; +import { npSetup, npStart } from 'ui/new_platform'; import { i18n } from '@kbn/i18n'; import chrome from 'ui/chrome'; import { metadata } from 'ui/metadata'; import { take } from 'rxjs/operators'; -import { JOBS_LIST_PATH } from './management_urls'; import { setDependencyCache } from '../util/dependency_cache'; -import './jobs_list'; +import { renderApp } from './jobs_list'; import { LicensingPluginSetup, LICENSE_CHECK_STATE, } from '../../../../../../plugins/licensing/public'; +import { ManagementSetup } from '../../../../../../../src/plugins/management/public'; import { PLUGIN_ID } from '../../../common/constants/app'; import { MINIMUM_FULL_LICENSE } from '../../../common/license'; @@ -36,11 +35,10 @@ const plugins = npSetup.plugins as PluginsSetupExtended; const licensing = plugins.licensing.license$.pipe(take(1)); licensing.subscribe(license => { if (license.check(PLUGIN_ID, MINIMUM_FULL_LICENSE).state === LICENSE_CHECK_STATE.Valid) { - initManagementSection(); + initManagementSection(plugins.management); } }); - -function initManagementSection() { +function initManagementSection(management: ManagementSetup) { const legacyBasePath = { prepend: chrome.addBasePath, get: chrome.getBasePath, @@ -54,22 +52,26 @@ function initManagementSection() { setDependencyCache({ docLinks: legacyDocLinks as any, basePath: legacyBasePath as any, + http: npStart.core.http, }); - management.register('ml', { - display: i18n.translate('xpack.ml.management.mlTitle', { + const mlSection = management.sections.register({ + id: 'ml', + title: i18n.translate('xpack.ml.management.mlTitle', { defaultMessage: 'Machine Learning', }), order: 100, icon: 'machineLearningApp', }); - management.getSection('ml').register('jobsList', { - name: 'jobsListLink', - order: 10, - display: i18n.translate('xpack.ml.management.jobsListTitle', { + mlSection.registerApp({ + id: 'jobsListLink', + title: i18n.translate('xpack.ml.management.jobsListTitle', { defaultMessage: 'Jobs list', }), - url: `#${JOBS_LIST_PATH}`, + order: 10, + async mount(params) { + return renderApp(params.element, {}); + }, }); } diff --git a/x-pack/legacy/plugins/ml/public/application/management/jobs_list/index.ts b/x-pack/legacy/plugins/ml/public/application/management/jobs_list/index.ts index b88138d139f60..3b3046aa5ca68 100644 --- a/x-pack/legacy/plugins/ml/public/application/management/jobs_list/index.ts +++ b/x-pack/legacy/plugins/ml/public/application/management/jobs_list/index.ts @@ -4,52 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ -import ReactDOM, { render, unmountComponentAtNode } from 'react-dom'; +import ReactDOM, { 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'; -import { JobsListPage, AccessDeniedPage } from './components'; -import { getJobsListBreadcrumbs } from '../breadcrumbs'; +// import { canGetManagementMlJobs } from '../../privilege/check_privilege'; +// import { JOBS_LIST_PATH, ACCESS_DENIED_PATH } from '../management_urls'; +// import { JobsListPage, AccessDeniedPage } from './components'; +import { JobsListPage } from './components'; +// import { getJobsListBreadcrumbs } from '../breadcrumbs'; -const template = ` -
-`; +export const renderApp = (element: HTMLElement, appDependencies: any) => { + // const { mlFeatureEnabledInSpace } = checkPrivilege; + const mlFeatureEnabledInSpace = true; + ReactDOM.render( + React.createElement(JobsListPage, { isMlEnabledInSpace: mlFeatureEnabledInSpace }), + element + ); -routes.when(JOBS_LIST_PATH, { - template, - k7Breadcrumbs: getJobsListBreadcrumbs, - resolve: { - checkPrivilege: canGetManagementMlJobs, - }, - 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'); - ReactDOM.render( - React.createElement(JobsListPage, { isMlEnabledInSpace: mlFeatureEnabledInSpace }), - element - ); - }); - }, -}); - -routes.when(ACCESS_DENIED_PATH, { - template, - k7Breadcrumbs: getJobsListBreadcrumbs, - controller($scope) { - $scope.$on('$destroy', () => { - const elem = document.getElementById('kibanaManagementMLSection'); - if (elem) unmountComponentAtNode(elem); - }); - $scope.$$postDigest(() => { - const element = document.getElementById('kibanaManagementMLSection'); - render(AccessDeniedPage(), element); - }); - }, -}); + return () => { + unmountComponentAtNode(element); + }; +}; diff --git a/x-pack/legacy/plugins/ml/public/plugin.ts b/x-pack/legacy/plugins/ml/public/plugin.ts index 7b3a5f6fadfac..ebbe6a11e8efd 100644 --- a/x-pack/legacy/plugins/ml/public/plugin.ts +++ b/x-pack/legacy/plugins/ml/public/plugin.ts @@ -8,14 +8,14 @@ import { Plugin, CoreStart, CoreSetup } from 'src/core/public'; import { MlDependencies } from './application/app'; export class MlPlugin implements Plugin { - setup(core: CoreSetup, { data, security, licensing }: MlDependencies) { + setup(core: CoreSetup, { data, security, licensing, management }: MlDependencies) { core.application.register({ id: 'ml', title: 'Machine learning', async mount(context, params) { const [coreStart, depsStart] = await core.getStartServices(); - const { renderApp } = await import('./application/app'); - return renderApp(coreStart, depsStart, { + const { renderApp: renderMlApp } = await import('./application/app'); + return renderMlApp(coreStart, depsStart, { element: params.element, appBasePath: params.appBasePath, onAppLeave: params.onAppLeave, @@ -23,6 +23,7 @@ export class MlPlugin implements Plugin { data, security, licensing, + management, }); }, }); From 972e4553aea624ddcd0d725d20a6343eba903f68 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Wed, 11 Mar 2020 13:17:24 +0100 Subject: [PATCH 2/3] [ML] Fix breadcrumb, permissions check, menu item. --- .../application/management/breadcrumbs.ts | 2 - .../ml/public/application/management/index.ts | 17 ++++++--- .../jobs_list_page/jobs_list_page.tsx | 37 +++++++++++++++---- .../application/management/jobs_list/index.ts | 11 +----- .../application/privilege/check_privilege.ts | 4 +- .../services/ml_api_service/index.js | 3 +- 6 files changed, 46 insertions(+), 28 deletions(-) diff --git a/x-pack/legacy/plugins/ml/public/application/management/breadcrumbs.ts b/x-pack/legacy/plugins/ml/public/application/management/breadcrumbs.ts index d3bc498c50f82..b68f5d0be6645 100644 --- a/x-pack/legacy/plugins/ml/public/application/management/breadcrumbs.ts +++ b/x-pack/legacy/plugins/ml/public/application/management/breadcrumbs.ts @@ -5,12 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { MANAGEMENT_BREADCRUMB } from 'ui/management/breadcrumbs'; import { JOBS_LIST_PATH } from './management_urls'; export function getJobsListBreadcrumbs() { return [ - MANAGEMENT_BREADCRUMB, { text: i18n.translate('xpack.ml.jobsList.breadcrumb', { defaultMessage: 'Jobs', diff --git a/x-pack/legacy/plugins/ml/public/application/management/index.ts b/x-pack/legacy/plugins/ml/public/application/management/index.ts index 9f2d489d6224c..d3dd1e4227531 100644 --- a/x-pack/legacy/plugins/ml/public/application/management/index.ts +++ b/x-pack/legacy/plugins/ml/public/application/management/index.ts @@ -15,16 +15,22 @@ import { i18n } from '@kbn/i18n'; import chrome from 'ui/chrome'; import { metadata } from 'ui/metadata'; import { take } from 'rxjs/operators'; -import { setDependencyCache } from '../util/dependency_cache'; -import { renderApp } from './jobs_list'; + +import { ManagementSetup } from '../../../../../../../src/plugins/management/public'; + import { LicensingPluginSetup, LICENSE_CHECK_STATE, } from '../../../../../../plugins/licensing/public'; -import { ManagementSetup } from '../../../../../../../src/plugins/management/public'; + import { PLUGIN_ID } from '../../../common/constants/app'; import { MINIMUM_FULL_LICENSE } from '../../../common/license'; +import { setDependencyCache } from '../util/dependency_cache'; + +import { getJobsListBreadcrumbs } from './breadcrumbs'; +import { renderApp } from './jobs_list'; + type PluginsSetupExtended = typeof npSetup.plugins & { // adds licensing which isn't in the PluginsSetup interface, but does exist licensing: LicensingPluginSetup; @@ -70,8 +76,9 @@ function initManagementSection(management: ManagementSetup) { defaultMessage: 'Jobs list', }), order: 10, - async mount(params) { - return renderApp(params.element, {}); + async mount({ element, setBreadcrumbs }) { + setBreadcrumbs(getJobsListBreadcrumbs()); + return renderApp(element, {}); }, }); } diff --git a/x-pack/legacy/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx b/x-pack/legacy/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx index a987ed7feeee9..fcf95f9e9fca0 100644 --- a/x-pack/legacy/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx +++ b/x-pack/legacy/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, FC, useState } from 'react'; +import React, { useEffect, useState, Fragment, FC } from 'react'; import { i18n } from '@kbn/i18n'; import { I18nContext } from 'ui/i18n'; import { @@ -18,15 +18,14 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; -import { getDocLinks } from '../../../../util/dependency_cache'; +import { checkGetManagementMlJobs } from '../../../../privilege/check_privilege'; + +import { getDocLinks } from '../../../../util/dependency_cache'; // @ts-ignore undeclared module import { JobsListView } from '../../../../jobs/jobs_list/components/jobs_list_view/index'; import { DataFrameAnalyticsList } from '../../../../data_frame_analytics/pages/analytics_management/components/analytics_list'; -interface Props { - isMlEnabledInSpace: boolean; -} interface Tab { id: string; name: string; @@ -65,11 +64,33 @@ function getTabs(isMlEnabledInSpace: boolean): Tab[] { ]; } -export const JobsListPage: FC = ({ isMlEnabledInSpace }) => { - const docLinks = getDocLinks(); - const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; +export const JobsListPage: FC = () => { + const [initialized, setInitialized] = useState(false); + const [isMlEnabledInSpace, setIsMlEnabledInSpace] = useState(false); const tabs = getTabs(isMlEnabledInSpace); const [currentTabId, setCurrentTabId] = useState(tabs[0].id); + + const check = async () => { + try { + const checkPrivilege = await checkGetManagementMlJobs(); + setInitialized(true); + setIsMlEnabledInSpace(checkPrivilege.mlFeatureEnabledInSpace); + } catch (e) { + // Silent fail, `checkGetManagementMlJobs()` should redirect when + // there are insufficient permissions. + } + }; + + useEffect(() => { + check(); + }, []); + + if (initialized === false) { + return null; + } + + const docLinks = getDocLinks(); + const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; const anomalyDetectionJobsUrl = `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-jobs.html`; const anomalyJobsUrl = `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfanalytics.html`; diff --git a/x-pack/legacy/plugins/ml/public/application/management/jobs_list/index.ts b/x-pack/legacy/plugins/ml/public/application/management/jobs_list/index.ts index 3b3046aa5ca68..1352fc1ef40b6 100644 --- a/x-pack/legacy/plugins/ml/public/application/management/jobs_list/index.ts +++ b/x-pack/legacy/plugins/ml/public/application/management/jobs_list/index.ts @@ -6,19 +6,10 @@ import ReactDOM, { unmountComponentAtNode } from 'react-dom'; import React from 'react'; -// import { canGetManagementMlJobs } from '../../privilege/check_privilege'; -// import { JOBS_LIST_PATH, ACCESS_DENIED_PATH } from '../management_urls'; -// import { JobsListPage, AccessDeniedPage } from './components'; import { JobsListPage } from './components'; -// import { getJobsListBreadcrumbs } from '../breadcrumbs'; export const renderApp = (element: HTMLElement, appDependencies: any) => { - // const { mlFeatureEnabledInSpace } = checkPrivilege; - const mlFeatureEnabledInSpace = true; - ReactDOM.render( - React.createElement(JobsListPage, { isMlEnabledInSpace: mlFeatureEnabledInSpace }), - element - ); + ReactDOM.render(React.createElement(JobsListPage), element); return () => { unmountComponentAtNode(element); diff --git a/x-pack/legacy/plugins/ml/public/application/privilege/check_privilege.ts b/x-pack/legacy/plugins/ml/public/application/privilege/check_privilege.ts index ec9695a2ce668..4de8c6eb703ff 100644 --- a/x-pack/legacy/plugins/ml/public/application/privilege/check_privilege.ts +++ b/x-pack/legacy/plugins/ml/public/application/privilege/check_privilege.ts @@ -14,8 +14,8 @@ import { ACCESS_DENIED_PATH } from '../management/management_urls'; 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() { - return new Promise((resolve, reject) => { +export function checkGetManagementMlJobs() { + return new Promise<{ mlFeatureEnabledInSpace: boolean }>((resolve, reject) => { getManageMlPrivileges().then( ({ capabilities, isPlatinumOrTrialLicense, mlFeatureEnabledInSpace }) => { privileges = capabilities; diff --git a/x-pack/legacy/plugins/ml/public/application/services/ml_api_service/index.js b/x-pack/legacy/plugins/ml/public/application/services/ml_api_service/index.js index 688abd1383ecb..5b2f213ffba9f 100644 --- a/x-pack/legacy/plugins/ml/public/application/services/ml_api_service/index.js +++ b/x-pack/legacy/plugins/ml/public/application/services/ml_api_service/index.js @@ -220,7 +220,8 @@ export const ml = { checkManageMLPrivileges() { return http({ - url: `${basePath()}/ml_capabilities?ignoreSpaces=true`, + // TODO Fix http service parameters + url: `${basePath()}/ml_capabilities`, // '?ignoreSpaces=true' method: 'GET', }); }, From 03c913d7650030ce2897383da9cc9aa15742b203 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Wed, 11 Mar 2020 13:45:03 +0100 Subject: [PATCH 3/3] [ML] Remove unnecessary management dependency. Re-add wrapping id to fix SCSS. --- x-pack/legacy/plugins/ml/public/application/app.tsx | 2 -- .../jobs_list/components/jobs_list_page/jobs_list_page.tsx | 2 +- x-pack/legacy/plugins/ml/public/plugin.ts | 3 +-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/x-pack/legacy/plugins/ml/public/application/app.tsx b/x-pack/legacy/plugins/ml/public/application/app.tsx index a8ad93670cdb7..18545f31f03c7 100644 --- a/x-pack/legacy/plugins/ml/public/application/app.tsx +++ b/x-pack/legacy/plugins/ml/public/application/app.tsx @@ -12,7 +12,6 @@ import 'ace'; import { AppMountParameters, CoreStart } from 'kibana/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { ManagementSetup } from 'src/plugins/management/public'; import { SecurityPluginSetup } from '../../../../../plugins/security/public'; import { LicensingPluginSetup } from '../../../../../plugins/licensing/public'; @@ -26,7 +25,6 @@ export interface MlDependencies extends AppMountParameters { data: DataPublicPluginStart; security: SecurityPluginSetup; licensing: LicensingPluginSetup; - management: ManagementSetup; } interface AppProps { diff --git a/x-pack/legacy/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx b/x-pack/legacy/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx index fcf95f9e9fca0..f3080dcece989 100644 --- a/x-pack/legacy/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx +++ b/x-pack/legacy/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx @@ -119,7 +119,7 @@ export const JobsListPage: FC = () => { return ( - + diff --git a/x-pack/legacy/plugins/ml/public/plugin.ts b/x-pack/legacy/plugins/ml/public/plugin.ts index ebbe6a11e8efd..1ef700f55b4db 100644 --- a/x-pack/legacy/plugins/ml/public/plugin.ts +++ b/x-pack/legacy/plugins/ml/public/plugin.ts @@ -8,7 +8,7 @@ import { Plugin, CoreStart, CoreSetup } from 'src/core/public'; import { MlDependencies } from './application/app'; export class MlPlugin implements Plugin { - setup(core: CoreSetup, { data, security, licensing, management }: MlDependencies) { + setup(core: CoreSetup, { data, security, licensing }: MlDependencies) { core.application.register({ id: 'ml', title: 'Machine learning', @@ -23,7 +23,6 @@ export class MlPlugin implements Plugin { data, security, licensing, - management, }); }, });