[ML] Replacing angular routing (#51842) (#52770)

* [ML] Replacing angular routing

* removing old files

* changing overview

* renaming overview route

* adding df analytics routes

* adding timeseriesexplorer route

* removing old files

* adding route for explorer

* adding access denied page

* adding module view or create redirect

* fixing job cloning

* adding breadcrumb system

* removing old breadcrumbs files

* fix include

* enabling management section

* injecting app dependencies

* fixing missed dependencies

* fixing saved searches

* fixing type errors

* removing included data start

* code clean up

* updating translations

* fixing router test failures

* fixing functional tests

* removing last use of SavedSearch

* removing comment

* fixing bug in line chart query

* improving saved search jobs

* fixing data viz functional test

* adding comment

* dealing with time range error

* removing unnecessary chrome imports

* cleaning up code

* moving resolver to own file

* changes based on review

* fixing index data viz on basic license

* fixing edit calendar

* adding create job breadcrumb

* fixing results appstate

* fixing management links

* updating new job constants file

* fixing rebase conflicts

* removing commented out code

* adding additional text to the resolver error
This commit is contained in:
James Gowdy 2019-12-11 17:49:04 +00:00 committed by GitHub
parent 57303bead6
commit 9bf0bd3f1a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
179 changed files with 2198 additions and 2437 deletions

View file

@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export const API_BASE_PATH = '/api/transform/';

View file

@ -9,6 +9,5 @@
// indices and aliases exist. Based on that the final setting will be available
// as an injectedVar on the client side and can be accessed like:
//
// import chrome from 'ui/chrome';
// const mlAnnotationsEnabled = chrome.getInjected('mlAnnotationsEnabled', false);
export const FEATURE_ANNOTATIONS_ENABLED = true;

View file

@ -6,6 +6,8 @@
// custom edits or fixes for default kibana types which are incomplete
import { SavedObjectAttributes, SimpleSavedObject } from 'kibana/public';
export type IndexPatternTitle = string;
export type callWithRequestType = (action: string, params?: any) => Promise<any>;
@ -14,3 +16,12 @@ export interface Route {
id: string;
k7Breadcrumbs: () => any;
}
export type IndexPatternSavedObject = SimpleSavedObject<SavedObjectAttributes>;
export type SavedSearchSavedObject = SimpleSavedObject<SavedObjectAttributes>;
export function isSavedSearchSavedObject(
ss: SavedSearchSavedObject | null
): ss is SavedSearchSavedObject {
return ss !== null;
}

View file

@ -12,7 +12,7 @@ import numeral from '@elastic/numeral';
import { ALLOWED_DATA_UNITS, JOB_ID_MAX_LENGTH } from '../constants/validation';
import { parseInterval } from './parse_interval';
import { maxLengthValidator } from './validators';
import { CREATED_BY_LABEL } from '../../public/application/jobs/new_job/common/job_creator/util/constants';
import { CREATED_BY_LABEL } from '../../common/constants/new_job';
// work out the default frequency based on the bucket_span in seconds
export function calculateDatafeedFrequencyDefaultSeconds(bucketSpanSeconds) {

View file

@ -41,9 +41,9 @@ export const ml = (kibana: any) => {
}),
icon: 'plugins/ml/application/ml.svg',
euiIconType: 'machineLearningApp',
main: 'plugins/ml/application/app',
main: 'plugins/ml/legacy',
},
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
styleSheetPaths: resolve(__dirname, 'public/application/index.scss'),
hacks: ['plugins/ml/application/hacks/toggle_app_link_in_nav'],
savedObjectSchemas: {
'ml-telemetry': {

View file

@ -0,0 +1,8 @@
{
"id": "ml",
"version": "0.0.1",
"kibanaVersion": "kibana",
"configPath": ["ml"],
"server": true,
"ui": true
}

View file

@ -4,36 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import uiRoutes from 'ui/routes';
import { I18nContext } from 'ui/i18n';
// @ts-ignore
import { uiModules } from 'ui/modules';
import { AccessDeniedPage } from './page';
const module = uiModules.get('apps/ml', ['react']);
const template = `<access-denied />`;
uiRoutes.when('/access-denied', {
template,
});
module.directive('accessDenied', function() {
return {
scope: {},
restrict: 'E',
link: async (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
ReactDOM.render(
<I18nContext>{React.createElement(AccessDeniedPage)}</I18nContext>,
element[0]
);
element.on('$destroy', () => {
ReactDOM.unmountComponentAtNode(element[0]);
scope.$destroy();
});
},
};
});
export { Page } from './page';

View file

@ -21,7 +21,7 @@ import {
} from '@elastic/eui';
import { NavigationMenu } from '../components/navigation_menu';
export const AccessDeniedPage = () => (
export const Page = () => (
<Fragment>
<NavigationMenu tabId="access-denied" />
<EuiPage>

View file

@ -1,36 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import 'uiExports/savedObjectTypes';
import 'ui/autoload/all';
// needed to make syntax highlighting work in ace editors
import 'ace';
import './access_denied';
import './jobs';
import './overview';
import './services/calendar_service';
import './data_frame_analytics';
import './datavisualizer';
import './explorer';
import './timeseriesexplorer';
import './components/navigation_menu';
import './components/loading_indicator';
import './settings';
import uiRoutes from 'ui/routes';
if (typeof uiRoutes.enable === 'function') {
uiRoutes.enable();
}
uiRoutes
.otherwise({
redirectTo: '/overview'
});

View file

@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { FC } from 'react';
import ReactDOM from 'react-dom';
import 'uiExports/savedObjectTypes';
import 'ui/autoload/all';
// needed to make syntax highlighting work in ace editors
import 'ace';
import { AppMountContext, AppMountParameters } from 'kibana/public';
import {
IndexPatternsContract,
Plugin as DataPlugin,
} from '../../../../../../src/plugins/data/public';
import { KibanaConfigTypeFix } from './contexts/kibana';
import { MlRouter } from './routing';
export interface MlDependencies extends AppMountParameters {
npData: ReturnType<DataPlugin['start']>;
indexPatterns: IndexPatternsContract;
}
interface AppProps {
context: AppMountContext;
indexPatterns: IndexPatternsContract;
}
const App: FC<AppProps> = ({ context, indexPatterns }) => {
const config = (context.core.uiSettings as never) as KibanaConfigTypeFix; // TODO - make this UiSettingsClientContract, get rid of KibanaConfigTypeFix
return (
<MlRouter
config={config}
setBreadcrumbs={context.core.chrome.setBreadcrumbs}
indexPatterns={indexPatterns}
/>
);
};
export const renderApp = (context: AppMountContext, { element, indexPatterns }: MlDependencies) => {
ReactDOM.render(<App context={context} indexPatterns={indexPatterns} />, element);
return () => ReactDOM.unmountComponentAtNode(element);
};

View file

@ -35,7 +35,6 @@ import {
} from '@elastic/eui/lib/services';
import { formatDate } from '@elastic/eui/lib/services/format';
import chrome from 'ui/chrome';
import { addItemToRecentlyAccessed } from '../../../util/recently_accessed';
import { ml } from '../../../services/ml_api_service';
@ -206,7 +205,7 @@ const AnnotationsTable = injectI18n(class AnnotationsTable extends Component {
const url = `?_g=${_g}&_a=${_a}`;
addItemToRecentlyAccessed('timeseriesexplorer', job.job_id, url);
window.open(`${chrome.getBasePath()}/app/ml#/timeseriesexplorer${url}`, '_self');
window.open(`#/timeseriesexplorer${url}`, '_self');
}
onMouseOverRow = (record) => {

View file

@ -205,7 +205,7 @@ export const LinksMenu = injectI18n(class LinksMenu extends Component {
});
// Need to encode the _a parameter in case any entities contain unsafe characters such as '+'.
let path = `${chrome.getBasePath()}/app/ml#/timeseriesexplorer`;
let path = '#/timeseriesexplorer';
path += `?_g=${_g}&_a=${encodeURIComponent(_a)}`;
window.open(path, '_blank');
}

View file

@ -7,11 +7,11 @@
import { FC } from 'react';
import { IndexPattern } from 'ui/index_patterns';
import { SavedSearch } from 'src/legacy/core_plugins/kibana/public/discover/types';
import { SavedSearchSavedObject } from '../../../../common/types/kibana';
declare const DataRecognizer: FC<{
indexPattern: IndexPattern;
savedSearch?: SavedSearch;
savedSearch?: SavedSearchSavedObject | null;
results: {
count: number;
onChange?: Function;

View file

@ -20,7 +20,7 @@ export const RecognizedResult = ({
indexPattern,
savedSearch
}) => {
const id = (savedSearch === undefined || savedSearch.id === undefined) ?
const id = (savedSearch === null) ?
`index=${indexPattern.id}` :
`savedSearchId=${savedSearch.id}`;

View file

@ -7,7 +7,6 @@
import React, { FC, useState } from 'react';
import { EuiTabs, EuiTab, EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import chrome from 'ui/chrome';
import { TabId } from './navigation_menu';
export interface Tab {
@ -82,7 +81,7 @@ export const MainTabs: FC<Props> = ({ tabId, disableLinks }) => {
return (
<EuiLink
data-test-subj={testSubject + (id === selectedTabId ? ' selected' : '')}
href={`${chrome.getBasePath()}/app/ml#/${defaultPathId}`}
href={`#/${defaultPathId}`}
key={`${id}-key`}
color="text"
>

View file

@ -7,7 +7,6 @@
import React, { FC, useState } from 'react';
import { EuiTabs, EuiTab, EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import chrome from 'ui/chrome';
import { Tab } from './main_tabs';
import { TabId } from './navigation_menu';
@ -84,7 +83,7 @@ export const Tabs: FC<Props> = ({ tabId, mainTabId, disableLinks }) => {
data-test-subj={
TAB_TEST_SUBJECT[id as TAB_TEST_SUBJECTS] + (id === selectedTabId ? ' selected' : '')
}
href={`${chrome.getBasePath()}/app/ml#/${id}`}
href={`#/${id}`}
key={`${id}-key`}
color="text"
>

View file

@ -4,14 +4,23 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { searchSourceMock } from '../../../../../../../../../src/legacy/ui/public/courier/search_source/mocks';
import { SearchSourceContract } from '../../../../../../../../../src/legacy/ui/public/courier';
export const savedSearchMock = {
export const savedSearchMock: any = {
id: 'the-saved-search-id',
title: 'the-saved-search-title',
searchSource: searchSourceMock as SearchSourceContract,
columns: [],
sort: [],
destroy: () => {},
type: 'search',
attributes: {
title: 'the-saved-search-title',
kibanaSavedObjectMeta: {
searchSourceJSON:
'{"highlightAll":true,"version":true,"query":{"query":"foo : \\"bar\\" ","language":"kuery"},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}',
},
},
references: [
{
name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
type: 'index-pattern',
id: 'the-index-pattern-id',
},
],
migrationVersion: { search: '7.5.0' },
error: undefined,
};

View file

@ -7,12 +7,11 @@
import React from 'react';
import { KibanaConfig } from 'src/legacy/server/kbn_server';
import { SavedSearch } from 'src/legacy/core_plugins/kibana/public/discover/types';
import {
IndexPattern,
IndexPatternsContract,
} from '../../../../../../../../src/plugins/data/public';
import { SavedSearchSavedObject } from '../../../../common/types/kibana';
// set() method is missing in original d.ts
export interface KibanaConfigTypeFix extends KibanaConfig {
@ -21,8 +20,8 @@ export interface KibanaConfigTypeFix extends KibanaConfig {
export interface KibanaContextValue {
combinedQuery: any;
currentIndexPattern: IndexPattern;
currentSavedSearch: SavedSearch;
currentIndexPattern: IndexPattern; // TODO this should be IndexPattern or null
currentSavedSearch: SavedSearchSavedObject | null;
indexPatterns: IndexPatternsContract;
kibanaConfig: KibanaConfigTypeFix;
}

View file

@ -1,21 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { ML_BREADCRUMB } from '../../breadcrumbs';
export function getDataFrameAnalyticsBreadcrumbs() {
return [
ML_BREADCRUMB,
{
text: i18n.translate('xpack.ml.dataFrameAnalyticsBreadcrumbs.dataFrameLabel', {
defaultMessage: 'Data Frame Analytics',
}),
href: '',
},
];
}

View file

@ -1,69 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
// @ts-ignore
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
import { I18nContext } from 'ui/i18n';
import { IndexPatternsContract } from '../../../../../../../../../src/plugins/data/public';
import { InjectorService } from '../../../../../common/types/angular';
import { createSearchItems } from '../../../jobs/new_job/utils/new_job_utils';
import { KibanaConfigTypeFix, KibanaContext } from '../../../contexts/kibana';
import { Page } from './page';
module.directive('mlDataFrameAnalyticsExploration', ($injector: InjectorService) => {
return {
scope: {},
restrict: 'E',
link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
const globalState = $injector.get<any>('globalState');
globalState.fetch();
const indexPatterns = $injector.get<IndexPatternsContract>('indexPatterns');
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
const $route = $injector.get<any>('$route');
const { indexPattern, savedSearch, combinedQuery } = createSearchItems(
kibanaConfig,
$route.current.locals.indexPattern,
$route.current.locals.savedSearch
);
const kibanaContext = {
combinedQuery,
currentIndexPattern: indexPattern,
currentSavedSearch: savedSearch,
indexPatterns,
kibanaConfig,
};
ReactDOM.render(
<I18nContext>
<KibanaContext.Provider value={kibanaContext}>
<Page
jobId={globalState.ml.jobId}
analysisType={globalState.ml.analysisType}
jobStatus={globalState.ml.jobStatus}
/>
</KibanaContext.Provider>
</I18nContext>,
element[0]
);
element.on('$destroy', () => {
ReactDOM.unmountComponentAtNode(element[0]);
scope.$destroy();
});
},
};
});

View file

@ -4,5 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './directive';
export { Page } from './page';

View file

@ -1,30 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import uiRoutes from 'ui/routes';
import { checkFullLicense } from '../../../license/check_license';
import { checkGetJobsPrivilege } from '../../../privilege/check_privilege';
import {
loadCurrentIndexPattern,
loadCurrentSavedSearch,
loadIndexPatterns,
} from '../../../util/index_utils';
import { getDataFrameAnalyticsBreadcrumbs } from '../../breadcrumbs';
const template = `<ml-data-frame-analytics-exploration />`;
uiRoutes.when('/data_frame_analytics/exploration?', {
template,
k7Breadcrumbs: getDataFrameAnalyticsBreadcrumbs,
resolve: {
CheckLicense: checkFullLicense,
privileges: checkGetJobsPrivilege,
indexPattern: loadCurrentIndexPattern,
indexPatterns: loadIndexPatterns,
savedSearch: loadCurrentSavedSearch,
},
});

View file

@ -1,61 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
// @ts-ignore
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
import { I18nContext } from 'ui/i18n';
import { InjectorService } from '../../../../../common/types/angular';
import { createSearchItems } from '../../../jobs/new_job/utils/new_job_utils';
import { IndexPatternsContract } from '../../../../../../../../../src/plugins/data/public';
import { KibanaConfigTypeFix, KibanaContext } from '../../../contexts/kibana';
import { Page } from './page';
module.directive('mlDataFrameAnalyticsManagement', ($injector: InjectorService) => {
return {
scope: {},
restrict: 'E',
link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
const indexPatterns = $injector.get<IndexPatternsContract>('indexPatterns');
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
const $route = $injector.get<any>('$route');
const { indexPattern, savedSearch, combinedQuery } = createSearchItems(
kibanaConfig,
$route.current.locals.indexPattern,
$route.current.locals.savedSearch
);
const kibanaContext = {
combinedQuery,
currentIndexPattern: indexPattern,
currentSavedSearch: savedSearch,
indexPatterns,
kibanaConfig,
};
ReactDOM.render(
<I18nContext>
<KibanaContext.Provider value={kibanaContext}>
<Page />
</KibanaContext.Provider>
</I18nContext>,
element[0]
);
element.on('$destroy', () => {
ReactDOM.unmountComponentAtNode(element[0]);
scope.$destroy();
});
},
};
});

View file

@ -4,7 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './list';
import './edit';
export { Page } from './page';

View file

@ -1,29 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import uiRoutes from 'ui/routes';
import { checkFullLicense } from '../../../license/check_license';
import { checkGetJobsPrivilege } from '../../../privilege/check_privilege';
import { loadMlServerInfo } from '../../../services/ml_server_info';
import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
import { loadCurrentIndexPattern, loadCurrentSavedSearch } from '../../../util/index_utils';
import { getDataFrameAnalyticsBreadcrumbs } from '../../breadcrumbs';
const template = `<ml-data-frame-analytics-management />`;
uiRoutes.when('/data_frame_analytics/?', {
template,
k7Breadcrumbs: getDataFrameAnalyticsBreadcrumbs,
resolve: {
CheckLicense: checkFullLicense,
privileges: checkGetJobsPrivilege,
indexPattern: loadCurrentIndexPattern,
savedSearch: loadCurrentSavedSearch,
mlNodeCount: getMlNodeCount,
loadMlServerInfo,
},
});

View file

@ -1,13 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { ML_BREADCRUMB, DATA_VISUALIZER_BREADCRUMB } from '../../breadcrumbs';
export function getDataVisualizerBreadcrumbs() {
// Whilst top level nav menu with tabs remains,
// use root ML breadcrumb.
return [ML_BREADCRUMB, DATA_VISUALIZER_BREADCRUMB];
}

View file

@ -57,7 +57,7 @@ export const DatavisualizerSelector: FC = () => {
return (
<Fragment>
<NavigationMenu tabId="datavisualizer" />
<EuiPage restrictWidth={1000}>
<EuiPage restrictWidth={1000} data-test-subj="mlPageDataVisualizerSelector">
<EuiPageBody>
<EuiFlexGroup gutterSize="xl">
<EuiFlexItem grow={false}>
@ -145,7 +145,7 @@ export const DatavisualizerSelector: FC = () => {
footer={
<EuiButton
target="_self"
href="#datavisualizer_index_select"
href="#/datavisualizer_index_select"
data-test-subj="mlDataVisualizerSelectIndexButton"
>
<FormattedMessage

View file

@ -1,54 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import { I18nContext } from 'ui/i18n';
// @ts-ignore
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
import uiRoutes from 'ui/routes';
import { getDataVisualizerBreadcrumbs } from './breadcrumbs';
import { checkBasicLicense } from '../license/check_license';
import { checkFindFileStructurePrivilege } from '../privilege/check_privilege';
const template = `
<div class="euiSpacer euiSpacer--s" />
<datavisualizer-selector data-test-subj="mlPageDataVisualizerSelector" />
`;
uiRoutes.when('/datavisualizer', {
template,
k7Breadcrumbs: getDataVisualizerBreadcrumbs,
resolve: {
CheckLicense: checkBasicLicense,
privileges: checkFindFileStructurePrivilege,
},
});
import { DatavisualizerSelector } from './datavisualizer_selector';
module.directive('datavisualizerSelector', function() {
return {
scope: {},
restrict: 'E',
link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
ReactDOM.render(
<I18nContext>
<DatavisualizerSelector />
</I18nContext>,
element[0]
);
element.on('$destroy', () => {
ReactDOM.unmountComponentAtNode(element[0]);
scope.$destroy();
});
},
};
});

View file

@ -1,23 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { ML_BREADCRUMB, DATA_VISUALIZER_BREADCRUMB } from '../../../breadcrumbs';
export function getFileDataVisualizerBreadcrumbs() {
// Whilst top level nav menu with tabs remains,
// use root ML breadcrumb.
return [
ML_BREADCRUMB,
DATA_VISUALIZER_BREADCRUMB,
{
text: i18n.translate('xpack.ml.dataVisualizer.fileBasedLabel', {
defaultMessage: 'File',
}),
href: '',
},
];
}

View file

@ -359,7 +359,7 @@ export class ImportView extends Component {
}
async loadIndexPatternNames() {
await loadIndexPatterns();
await loadIndexPatterns(this.props.indexPatterns);
const indexPatternNames = getIndexPatternNames();
this.setState({ indexPatternNames });
}

View file

@ -121,7 +121,7 @@ export class ResultsLinks extends Component {
/>
}
description=""
href={`${uiChrome.getBasePath()}/app/ml#/jobs/new_job/step/job_type?index=${indexPatternId}${_g}`}
href={`#/jobs/new_job/step/job_type?index=${indexPatternId}${_g}`}
/>
</EuiFlexItem>
}
@ -137,7 +137,7 @@ export class ResultsLinks extends Component {
/>
}
description=""
href={`${uiChrome.getBasePath()}/app/ml#/jobs/new_job/datavisualizer?index=${indexPatternId}${_g}`}
href={`#/jobs/new_job/datavisualizer?index=${indexPatternId}${_g}`}
/>
</EuiFlexItem>
}

View file

@ -6,26 +6,22 @@
import React, { FC, Fragment } from 'react';
import { timefilter } from 'ui/timefilter';
import { IndexPatternsContract } from '../../../../../../../../src/plugins/data/public';
import { KibanaConfigTypeFix } from '../../contexts/kibana';
import { NavigationMenu } from '../../components/navigation_menu';
import { getIndexPatternsContract } from '../../util/index_utils';
// @ts-ignore
import { FileDataVisualizerView } from './components/file_datavisualizer_view/index';
export interface FileDataVisualizerPageProps {
indexPatterns: IndexPatternsContract;
kibanaConfig: KibanaConfigTypeFix;
}
export const FileDataVisualizerPage: FC<FileDataVisualizerPageProps> = ({
indexPatterns,
kibanaConfig,
}) => {
export const FileDataVisualizerPage: FC<FileDataVisualizerPageProps> = ({ kibanaConfig }) => {
timefilter.disableTimeRangeSelector();
timefilter.disableAutoRefreshSelector();
const indexPatterns = getIndexPatternsContract();
return (
<Fragment>
<NavigationMenu tabId="datavisualizer" />

View file

@ -1,68 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import { I18nContext } from 'ui/i18n';
// @ts-ignore
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
import uiRoutes from 'ui/routes';
import { KibanaConfigTypeFix } from '../../contexts/kibana';
import { getFileDataVisualizerBreadcrumbs } from './breadcrumbs';
import { InjectorService } from '../../../../common/types/angular';
import { checkBasicLicense } from '../../license/check_license';
import { checkFindFileStructurePrivilege } from '../../privilege/check_privilege';
import { getMlNodeCount } from '../../ml_nodes_check/check_ml_nodes';
import { loadMlServerInfo } from '../../services/ml_server_info';
import { loadIndexPatterns } from '../../util/index_utils';
import { FileDataVisualizerPage, FileDataVisualizerPageProps } from './file_datavisualizer';
import { IndexPatternsContract } from '../../../../../../../../src/plugins/data/public';
const template = `
<div class="euiSpacer euiSpacer--s" />
<file-datavisualizer-page />
`;
uiRoutes.when('/filedatavisualizer/?', {
template,
k7Breadcrumbs: getFileDataVisualizerBreadcrumbs,
resolve: {
CheckLicense: checkBasicLicense,
privileges: checkFindFileStructurePrivilege,
indexPatterns: loadIndexPatterns,
mlNodeCount: getMlNodeCount,
loadMlServerInfo,
},
});
module.directive('fileDatavisualizerPage', function($injector: InjectorService) {
return {
scope: {},
restrict: 'E',
link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
const indexPatterns = $injector.get<IndexPatternsContract>('indexPatterns');
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
const props: FileDataVisualizerPageProps = {
indexPatterns,
kibanaConfig,
};
ReactDOM.render(
<I18nContext>{React.createElement(FileDataVisualizerPage, props)}</I18nContext>,
element[0]
);
element.on('$destroy', () => {
ReactDOM.unmountComponentAtNode(element[0]);
scope.$destroy();
});
},
};
});

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './file_datavisualizer_directive';
export { FileDataVisualizerPage } from './file_datavisualizer';

View file

@ -4,6 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './directive';
import './file_based';
import './index_based';
export { DatavisualizerSelector } from './datavisualizer_selector';

View file

@ -1,27 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import {
ML_BREADCRUMB,
DATA_VISUALIZER_BREADCRUMB,
// @ts-ignore
} from '../../../breadcrumbs';
export function getDataVisualizerBreadcrumbs() {
// Whilst top level nav menu with tabs remains,
// use root ML breadcrumb.
return [
ML_BREADCRUMB,
DATA_VISUALIZER_BREADCRUMB,
{
text: i18n.translate('xpack.ml.dataFrameAnalyticsBreadcrumbs.indexLabel', {
defaultMessage: 'Index',
}),
href: '',
},
];
}

View file

@ -13,7 +13,6 @@ import { IndexPattern } from 'ui/index_patterns';
import { EuiPanel, EuiSpacer, EuiText, EuiTitle, EuiFlexGroup } from '@elastic/eui';
import { useUiChromeContext } from '../../../../contexts/ui/use_ui_chrome_context';
import { CreateJobLinkCard } from '../../../../components/create_job_link_card';
import { DataRecognizer } from '../../../../components/data_recognizer';
@ -31,12 +30,10 @@ export const ActionsPanel: FC<Props> = ({ indexPattern }) => {
},
};
const basePath = useUiChromeContext().getBasePath();
function openAdvancedJobWizard() {
// TODO - pass the search string to the advanced job page as well as the index pattern
// (add in with new advanced job wizard?)
window.open(`${basePath}/app/ml#/jobs/new_job/advanced?index=${indexPattern}`, '_self');
window.open(`#/jobs/new_job/advanced?index=${indexPattern}`, '_self');
}
// Note we use display:none for the DataRecognizer section as it needs to be
@ -87,7 +84,7 @@ export const ActionsPanel: FC<Props> = ({ indexPattern }) => {
'Use the full range of options to create a job for more advanced use cases',
})}
onClick={openAdvancedJobWizard}
href={`${basePath}/app/ml#/jobs/new_job/advanced?index=${indexPattern}`}
href={`#/jobs/new_job/advanced?index=${indexPattern}`}
/>
</EuiPanel>
);

View file

@ -1,61 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
// @ts-ignore
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
import { I18nContext } from 'ui/i18n';
import { IndexPatternsContract } from '../../../../../../../../src/plugins/data/public';
import { InjectorService } from '../../../../common/types/angular';
import { KibanaConfigTypeFix, KibanaContext } from '../../contexts/kibana/kibana_context';
import { createSearchItems } from '../../jobs/new_job/utils/new_job_utils';
import { Page } from './page';
module.directive('mlDataVisualizer', ($injector: InjectorService) => {
return {
scope: {},
restrict: 'E',
link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
const indexPatterns = $injector.get<IndexPatternsContract>('indexPatterns');
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
const $route = $injector.get<any>('$route');
const { indexPattern, savedSearch, combinedQuery } = createSearchItems(
kibanaConfig,
$route.current.locals.indexPattern,
$route.current.locals.savedSearch
);
const kibanaContext = {
combinedQuery,
currentIndexPattern: indexPattern,
currentSavedSearch: savedSearch,
indexPatterns,
kibanaConfig,
};
ReactDOM.render(
<I18nContext>
<KibanaContext.Provider value={kibanaContext}>
<Page />
</KibanaContext.Provider>
</I18nContext>,
element[0]
);
element.on('$destroy', () => {
ReactDOM.unmountComponentAtNode(element[0]);
scope.$destroy();
});
},
};
});

View file

@ -4,5 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './directive';
import './route';
export { Page } from './page';

View file

@ -31,7 +31,7 @@ import { FullTimeRangeSelector } from '../../components/full_time_range_selector
import { mlTimefilterRefresh$ } from '../../services/timefilter_refresh_service';
import { useKibanaContext, SavedSearchQuery } from '../../contexts/kibana';
import { kbnTypeToMLJobType } from '../../util/field_types_utils';
import { timeBasedIndexCheck } from '../../util/index_utils';
import { timeBasedIndexCheck, getQueryFromSavedSearch } from '../../util/index_utils';
import { TimeBuckets } from '../../util/time_buckets';
import { FieldRequestConfig, FieldVisConfig } from './common';
import { ActionsPanel } from './components/actions_panel';
@ -173,9 +173,8 @@ export const Page: FC = () => {
useEffect(() => {
// Check for a saved search being passed in.
const searchSource = currentSavedSearch.searchSource;
const query = searchSource.getField('query');
if (query !== undefined) {
if (currentSavedSearch !== null) {
const { query } = getQueryFromSavedSearch(currentSavedSearch);
const queryLanguage = query.language as SEARCH_QUERY_LANGUAGE;
const qryString = query.query;
let qry;

View file

@ -1,28 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
// @ts-ignore
import uiRoutes from 'ui/routes';
import { checkBasicLicense } from '../../license/check_license';
import { checkGetJobsPrivilege } from '../../privilege/check_privilege';
import { loadCurrentIndexPattern, loadCurrentSavedSearch } from '../../util/index_utils';
import { checkMlNodesAvailable } from '../../ml_nodes_check';
import { getDataVisualizerBreadcrumbs } from './breadcrumbs';
const template = `<ml-data-visualizer />`;
uiRoutes.when('/jobs/new_job/datavisualizer', {
template,
k7Breadcrumbs: getDataVisualizerBreadcrumbs,
resolve: {
CheckLicense: checkBasicLicense,
privileges: checkGetJobsPrivilege,
indexPattern: loadCurrentIndexPattern,
savedSearch: loadCurrentSavedSearch,
checkMlNodesAvailable,
},
});

View file

@ -1,23 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { ML_BREADCRUMB, ANOMALY_DETECTION_BREADCRUMB } from '../../breadcrumbs';
export function getAnomalyExplorerBreadcrumbs() {
// Whilst top level nav menu with tabs remains,
// use root ML breadcrumb.
return [
ML_BREADCRUMB,
ANOMALY_DETECTION_BREADCRUMB,
{
text: i18n.translate('xpack.ml.anomalyDetection.anomalyExplorerLabel', {
defaultMessage: 'Anomaly Explorer',
}),
href: '',
},
];
}

View file

@ -93,7 +93,7 @@ function mapSwimlaneOptionsToEuiOptions(options) {
}
const ExplorerPage = ({ children, jobSelectorProps, resizeRef }) => (
<div ref={resizeRef}>
<div ref={resizeRef} data-test-subj="mlPageAnomalyExplorer">
<NavigationMenu tabId="explorer" />
<JobSelector {...jobSelectorProps} />
{children}
@ -117,7 +117,6 @@ export const Explorer = injectI18n(injectObservablesAsProps(
};
_unsubscribeAll = new Subject();
// make sure dragSelect is only available if the mouse pointer is actually over a swimlane
disableDragSelectOnMouseLeave = true;

View file

@ -1,110 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
/*
* AngularJS directive wrapper for rendering Anomaly Explorer's React component.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import { Subscription } from 'rxjs';
import { IRootElementService, IRootScopeService, IScope } from 'angular';
// @ts-ignore
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml');
import { I18nContext } from 'ui/i18n';
import { State } from 'ui/state_management/state';
import { AppState as IAppState, AppStateClass } from 'ui/state_management/app_state';
import { jobSelectServiceFactory } from '../components/job_selector/job_select_service_utils';
import { interval$ } from '../components/controls/select_interval';
import { severity$ } from '../components/controls/select_severity';
import { showCharts$ } from '../components/controls/checkbox_showcharts';
import { subscribeAppStateToObservable } from '../util/app_state_utils';
import { Explorer } from './explorer';
import { explorerService } from './explorer_dashboard_service';
import { getExplorerDefaultAppState, ExplorerAppState } from './reducers';
interface ExplorerScope extends IScope {
appState: IAppState;
}
module.directive('mlAnomalyExplorer', function(
globalState: State,
$rootScope: IRootScopeService,
AppState: AppStateClass
) {
function link($scope: ExplorerScope, element: IRootElementService) {
const subscriptions = new Subscription();
const { jobSelectService$, unsubscribeFromGlobalState } = jobSelectServiceFactory(globalState);
ReactDOM.render(
<I18nContext>
<Explorer
{...{
globalState,
jobSelectService$,
}}
/>
</I18nContext>,
element[0]
);
// Initialize the AppState in which to store swimlane and filter settings.
// AppState is used to store state in the URL.
$scope.appState = new AppState(getExplorerDefaultAppState());
const { mlExplorerFilter, mlExplorerSwimlane } = $scope.appState;
// Pass the current URL AppState on to anomaly explorer's reactive state.
// After this hand-off, the appState stored in explorerState$ is the single
// source of truth.
explorerService.setAppState({ mlExplorerSwimlane, mlExplorerFilter });
// Now that appState in explorerState$ is the single source of truth,
// subscribe to it and update the actual URL appState on changes.
subscriptions.add(
explorerService.appState$.subscribe((appState: ExplorerAppState) => {
$scope.appState.fetch();
$scope.appState.mlExplorerFilter = appState.mlExplorerFilter;
$scope.appState.mlExplorerSwimlane = appState.mlExplorerSwimlane;
$scope.appState.save();
$scope.$applyAsync();
})
);
subscriptions.add(
subscribeAppStateToObservable(AppState, 'mlShowCharts', showCharts$, () =>
$rootScope.$applyAsync()
)
);
subscriptions.add(
subscribeAppStateToObservable(AppState, 'mlSelectInterval', interval$, () =>
$rootScope.$applyAsync()
)
);
subscriptions.add(
subscribeAppStateToObservable(AppState, 'mlSelectSeverity', severity$, () =>
$rootScope.$applyAsync()
)
);
element.on('$destroy', () => {
ReactDOM.unmountComponentAtNode(element[0]);
$scope.$destroy();
subscriptions.unsubscribe();
unsubscribeFromGlobalState();
});
}
return { link };
});

View file

@ -1,27 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import uiRoutes from 'ui/routes';
import '../components/controls';
import { checkFullLicense } from '../license/check_license';
import { checkGetJobsPrivilege } from '../privilege/check_privilege';
import { mlJobService } from '../services/job_service';
import { loadIndexPatterns } from '../util/index_utils';
import { getAnomalyExplorerBreadcrumbs } from './breadcrumbs';
uiRoutes.when('/explorer/?', {
template: `<ml-anomaly-explorer class="ml-explorer" data-test-subj="mlPageAnomalyExplorer" />`,
k7Breadcrumbs: getAnomalyExplorerBreadcrumbs,
resolve: {
CheckLicense: checkFullLicense,
privileges: checkGetJobsPrivilege,
indexPatterns: loadIndexPatterns,
jobs: mlJobService.loadJobsWrapper,
},
});

View file

@ -4,9 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
import '../explorer/explorer_dashboard_service';
import '../explorer/explorer_directive';
import '../explorer/explorer_route';
import '../explorer/explorer_charts';
import '../explorer/select_limit';
import '../components/job_selector';
export { Explorer } from './explorer';

View file

@ -0,0 +1,45 @@
// Should import both the EUI constants and any Kibana ones that are considered global
@import 'src/legacy/ui/public/styles/styling_constants';
// 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 {
// App level
@import 'app';
// Sub applications
@import 'data_frame_analytics/index';
@import 'datavisualizer/index';
@import 'explorer/index'; // SASSTODO: This file needs to be rewritten
@import 'jobs/index'; // SASSTODO: This collection of sass files has multiple problems
@import 'overview/index';
@import 'settings/index';
@import 'timeseriesexplorer/index';
// Components
@import 'components/annotations/annotation_description_list/index'; // SASSTODO: This file overwrites EUI directly
@import 'components/anomalies_table/index'; // SASSTODO: This file overwrites EUI directly
@import 'components/chart_tooltip/index';
@import 'components/controls/index';
@import 'components/entity_cell/index';
@import 'components/field_title_bar/index';
@import 'components/field_type_icon/index';
@import 'components/influencers_list/index';
@import 'components/items_grid/index';
@import 'components/job_selector/index';
@import 'components/loading_indicator/index'; // SASSTODO: This component should be replaced with EuiLoadingSpinner
@import 'components/navigation_menu/index';
@import 'components/rule_editor/index'; // SASSTODO: This file overwrites EUI directly
@import 'components/stats_bar/index';
// Hacks are last so they can overwrite anything above if needed
@import 'hacks';
}

View file

@ -1,112 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { Breadcrumb } from 'ui/chrome';
import {
ANOMALY_DETECTION_BREADCRUMB,
DATA_VISUALIZER_BREADCRUMB,
ML_BREADCRUMB,
} from '../../breadcrumbs';
export function getJobManagementBreadcrumbs(): Breadcrumb[] {
// Whilst top level nav menu with tabs remains,
// use root ML breadcrumb.
return [
ML_BREADCRUMB,
ANOMALY_DETECTION_BREADCRUMB,
{
text: i18n.translate('xpack.ml.anomalyDetection.jobManagementLabel', {
defaultMessage: 'Job Management',
}),
href: '',
},
];
}
export function getCreateJobBreadcrumbs(): Breadcrumb[] {
return [
ML_BREADCRUMB,
ANOMALY_DETECTION_BREADCRUMB,
{
text: i18n.translate('xpack.ml.jobsBreadcrumbs.createJobLabel', {
defaultMessage: 'Create job',
}),
href: '#/jobs/new_job',
},
];
}
export function getCreateSingleMetricJobBreadcrumbs(): Breadcrumb[] {
return [
...getCreateJobBreadcrumbs(),
{
text: i18n.translate('xpack.ml.jobsBreadcrumbs.singleMetricLabel', {
defaultMessage: 'Single metric',
}),
href: '',
},
];
}
export function getCreateMultiMetricJobBreadcrumbs(): Breadcrumb[] {
return [
...getCreateJobBreadcrumbs(),
{
text: i18n.translate('xpack.ml.jobsBreadcrumbs.multiMetricLabel', {
defaultMessage: 'Multi metric',
}),
href: '',
},
];
}
export function getCreatePopulationJobBreadcrumbs(): Breadcrumb[] {
return [
...getCreateJobBreadcrumbs(),
{
text: i18n.translate('xpack.ml.jobsBreadcrumbs.populationLabel', {
defaultMessage: 'Population',
}),
href: '',
},
];
}
export function getAdvancedJobConfigurationBreadcrumbs(): Breadcrumb[] {
return [
...getCreateJobBreadcrumbs(),
{
text: i18n.translate('xpack.ml.jobsBreadcrumbs.advancedConfigurationLabel', {
defaultMessage: 'Advanced configuration',
}),
href: '',
},
];
}
export function getCreateRecognizerJobBreadcrumbs($routeParams: any): Breadcrumb[] {
return [
...getCreateJobBreadcrumbs(),
{
text: $routeParams.id,
href: '',
},
];
}
export function getDataVisualizerIndexOrSearchBreadcrumbs(): Breadcrumb[] {
return [
ML_BREADCRUMB,
DATA_VISUALIZER_BREADCRUMB,
{
text: i18n.translate('xpack.ml.jobsBreadcrumbs.selectIndexOrSearchLabel', {
defaultMessage: 'Select index or search',
}),
href: '',
},
];
}

View file

@ -4,7 +4,5 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './pages/analytics_exploration/directive';
import './pages/analytics_exploration/route';
import './pages/analytics_management/directive';
import './pages/analytics_management/route';
export { JobsPage } from './jobs_list';
export {} from './new_job';

View file

@ -7,20 +7,20 @@
import PropTypes from 'prop-types';
import React from 'react';
import chrome from 'ui/chrome';
import {
EuiButtonIcon,
EuiToolTip,
} from '@elastic/eui';
import chrome from 'ui/chrome';
import { mlJobService } from '../../../../services/job_service';
import { injectI18n } from '@kbn/i18n/react';
export function getLink(location, jobs) {
const resultsPageUrl = mlJobService.createResultsUrlForJobs(jobs, location);
return `${chrome.getBasePath()}/app/${resultsPageUrl}`;
return `${chrome.getBasePath()}/app/ml${resultsPageUrl}`;
}
function ResultLinksUI({ jobs, intl }) {
@ -39,6 +39,7 @@ function ResultLinksUI({ jobs, intl }) {
const singleMetricVisible = (jobs.length < 2);
const singleMetricEnabled = (jobs.length === 1 && jobs[0].isSingleMetricViewerJob);
const jobActionsDisabled = (jobs.length === 1 && jobs[0].deleting === true);
return (
<React.Fragment>
{(singleMetricVisible) &&

View file

@ -6,7 +6,6 @@
import React from 'react';
import { EuiLink } from '@elastic/eui';
import chrome from 'ui/chrome';
import { detectorToString } from '../../../../util/string_utils';
import { formatValues, filterObjects } from './format_values';
import { i18n } from '@kbn/i18n';
@ -62,7 +61,7 @@ export function extractJobDetails(job) {
if (job.calendars) {
calendars.items = job.calendars.map(c => [
'',
<EuiLink href={`${chrome.getBasePath()}/app/ml#/settings/calendars_list/edit_calendar/${c}?_g=()`}>{c}</EuiLink>,
<EuiLink href={`#/settings/calendars_list/edit_calendar/${c}?_g=()`}>{c}</EuiLink>,
]);
// remove the calendars list from the general section
// so not to show it twice.

View file

@ -23,7 +23,6 @@ import {
EuiLoadingSpinner
} from '@elastic/eui';
import { formatDate, formatNumber } from '@elastic/eui/lib/services/format';
import chrome from 'ui/chrome';
import { FORECAST_REQUEST_STATE } from '../../../../../../../common/constants/states';
import { addItemToRecentlyAccessed } from '../../../../../util/recently_accessed';
@ -128,7 +127,7 @@ class ForecastsTableUI extends Component {
const url = `?_g=${_g}&_a=${_a}`;
addItemToRecentlyAccessed('timeseriesexplorer', this.props.job.job_id, url);
window.open(`${chrome.getBasePath()}/app/ml#/timeseriesexplorer${url}`, '_self');
window.open(`#/timeseriesexplorer${url}`, '_self');
}
render() {

View file

@ -67,15 +67,6 @@ export class JobsListView extends Component {
if (this.props.isManagementTable === true) {
this.refreshJobSummaryList(true);
} else {
// The advanced job wizard is still angularjs based and triggers
// broadcast events which it expects the jobs list to be subscribed to.
this.props.angularWrapperScope.$on('jobsUpdated', () => {
this.refreshJobSummaryList(true);
});
this.props.angularWrapperScope.$on('openCreateWatchWindow', (e, job) => { // eslint-disable-line no-unused-vars
this.showCreateWatchFlyout(job.job_id);
});
timefilter.disableTimeRangeSelector();
timefilter.enableAutoRefreshSelector();

View file

@ -1,59 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import ReactDOM from 'react-dom';
import React from 'react';
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
import { loadIndexPatterns } from '../../util/index_utils';
import { checkFullLicense } from '../../license/check_license';
import { checkGetJobsPrivilege } from '../../privilege/check_privilege';
import { getMlNodeCount } from '../../ml_nodes_check/check_ml_nodes';
import { getJobManagementBreadcrumbs } from '../../jobs/breadcrumbs';
import { loadMlServerInfo } from '../../services/ml_server_info';
import uiRoutes from 'ui/routes';
const template = `<jobs-page data-test-subj="mlPageJobManagement" />`;
uiRoutes
.when('/jobs/?', {
template,
k7Breadcrumbs: getJobManagementBreadcrumbs,
resolve: {
CheckLicense: checkFullLicense,
indexPatterns: loadIndexPatterns,
privileges: checkGetJobsPrivilege,
mlNodeCount: getMlNodeCount,
loadMlServerInfo,
}
});
import { JobsPage } from './jobs';
import { I18nContext } from 'ui/i18n';
module.directive('jobsPage', function () {
return {
scope: {},
restrict: 'E',
link: (scope, element) => {
ReactDOM.render(
<I18nContext>
<JobsPage angularWrapperScope={scope} />
</I18nContext>,
element[0]
);
element.on('$destroy', () => {
ReactDOM.unmountComponentAtNode(element[0]);
scope.$destroy();
});
}
};
});

View file

@ -4,7 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './jobs_list';
import './new_job';
export { JobsPage } from './jobs';

View file

@ -4,15 +4,18 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import React, { FC } from 'react';
import { NavigationMenu } from '../../components/navigation_menu';
// @ts-ignore
import { JobsListView } from './components/jobs_list_view';
export const JobsPage = (props) => (
<>
<NavigationMenu tabId="jobs" />
<JobsListView {...props} />
</>
);
export const JobsPage: FC<{ props?: any }> = props => {
return (
<div data-test-subj="mlPageJobManagement">
<NavigationMenu tabId="jobs" />
<JobsListView {...props} />
</div>
);
};

View file

@ -4,14 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { SavedSearch } from 'src/legacy/core_plugins/kibana/public/discover/types';
import { IndexPattern } from 'ui/index_patterns';
import { SavedSearchSavedObject } from '../../../../../../common/types/kibana';
import { JobCreator } from './job_creator';
import { Field, Aggregation, SplitField } from '../../../../../../common/types/fields';
import { Job, Datafeed, Detector, CustomRule } from './configs';
import { createBasicDetector } from './util/default_configs';
import { JOB_TYPE } from './util/constants';
import { JOB_TYPE } from '../../../../../../common/constants/new_job';
import { getRichDetectors } from './util/general';
import { isValidJson } from '../../../../../../common/util/validation_utils';
import { ml } from '../../../../services/ml_api_service';
@ -32,7 +32,11 @@ export class AdvancedJobCreator extends JobCreator {
private _richDetectors: RichDetector[] = [];
private _queryString: string;
constructor(indexPattern: IndexPattern, savedSearch: SavedSearch, query: object) {
constructor(
indexPattern: IndexPattern,
savedSearch: SavedSearchSavedObject | null,
query: object
) {
super(indexPattern, savedSearch, query);
this._queryString = JSON.stringify(this._datafeed_config.query);

View file

@ -5,7 +5,7 @@
*/
import { UrlConfig } from '../../../../../../../common/types/custom_urls';
import { CREATED_BY_LABEL } from '../util/constants';
import { CREATED_BY_LABEL } from '../../../../../../../common/constants/new_job';
export type JobId = string;
export type BucketSpan = string;

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { SavedSearch } from 'src/legacy/core_plugins/kibana/public/discover/types';
import { IndexPattern } from 'ui/index_patterns';
import { SavedSearchSavedObject } from '../../../../../../common/types/kibana';
import { UrlConfig } from '../../../../../../common/types/custom_urls';
import { IndexPatternTitle } from '../../../../../../common/types/kibana';
import { ML_JOB_AGGREGATION } from '../../../../../../common/constants/aggregation_types';
@ -15,7 +15,11 @@ import { Aggregation, Field } from '../../../../../../common/types/fields';
import { createEmptyJob, createEmptyDatafeed } from './util/default_configs';
import { mlJobService } from '../../../../services/job_service';
import { JobRunner, ProgressSubscriber } from '../job_runner';
import { JOB_TYPE, CREATED_BY_LABEL, SHARED_RESULTS_INDEX_NAME } from './util/constants';
import {
JOB_TYPE,
CREATED_BY_LABEL,
SHARED_RESULTS_INDEX_NAME,
} from '../../../../../../common/constants/new_job';
import { isSparseDataJob } from './util/general';
import { parseInterval } from '../../../../../../common/util/parse_interval';
import { Calendar } from '../../../../../../common/types/calendars';
@ -24,7 +28,7 @@ import { mlCalendarService } from '../../../../services/calendar_service';
export class JobCreator {
protected _type: JOB_TYPE = JOB_TYPE.SINGLE_METRIC;
protected _indexPattern: IndexPattern;
protected _savedSearch: SavedSearch;
protected _savedSearch: SavedSearchSavedObject | null;
protected _indexPatternTitle: IndexPatternTitle = '';
protected _job_config: Job;
protected _calendars: Calendar[];
@ -44,7 +48,11 @@ export class JobCreator {
stop: boolean;
} = { stop: false };
constructor(indexPattern: IndexPattern, savedSearch: SavedSearch, query: object) {
constructor(
indexPattern: IndexPattern,
savedSearch: SavedSearchSavedObject | null,
query: object
) {
this._indexPattern = indexPattern;
this._savedSearch = savedSearch;
this._indexPatternTitle = indexPattern.title;

View file

@ -4,18 +4,18 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { SavedSearch } from 'src/legacy/core_plugins/kibana/public/discover/types';
import { IndexPattern } from 'ui/index_patterns';
import { SavedSearchSavedObject } from '../../../../../../common/types/kibana';
import { SingleMetricJobCreator } from './single_metric_job_creator';
import { MultiMetricJobCreator } from './multi_metric_job_creator';
import { PopulationJobCreator } from './population_job_creator';
import { AdvancedJobCreator } from './advanced_job_creator';
import { JOB_TYPE } from './util/constants';
import { JOB_TYPE } from '../../../../../../common/constants/new_job';
export const jobCreatorFactory = (jobType: JOB_TYPE) => (
indexPattern: IndexPattern,
savedSearch: SavedSearch,
savedSearch: SavedSearchSavedObject | null,
query: object
) => {
let jc;

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { SavedSearch } from 'src/legacy/core_plugins/kibana/public/discover/types';
import { IndexPattern } from 'ui/index_patterns';
import { SavedSearchSavedObject } from '../../../../../../common/types/kibana';
import { JobCreator } from './job_creator';
import {
Field,
@ -15,7 +15,11 @@ import {
} from '../../../../../../common/types/fields';
import { Job, Datafeed, Detector } from './configs';
import { createBasicDetector } from './util/default_configs';
import { JOB_TYPE, CREATED_BY_LABEL, DEFAULT_MODEL_MEMORY_LIMIT } from './util/constants';
import {
JOB_TYPE,
CREATED_BY_LABEL,
DEFAULT_MODEL_MEMORY_LIMIT,
} from '../../../../../../common/constants/new_job';
import { ml } from '../../../../services/ml_api_service';
import { getRichDetectors } from './util/general';
@ -26,7 +30,11 @@ export class MultiMetricJobCreator extends JobCreator {
private _lastEstimatedModelMemoryLimit = DEFAULT_MODEL_MEMORY_LIMIT;
protected _type: JOB_TYPE = JOB_TYPE.MULTI_METRIC;
constructor(indexPattern: IndexPattern, savedSearch: SavedSearch, query: object) {
constructor(
indexPattern: IndexPattern,
savedSearch: SavedSearchSavedObject | null,
query: object
) {
super(indexPattern, savedSearch, query);
this.createdBy = CREATED_BY_LABEL.MULTI_METRIC;
}

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { SavedSearch } from 'src/legacy/core_plugins/kibana/public/discover/types';
import { IndexPattern } from 'ui/index_patterns';
import { SavedSearchSavedObject } from '../../../../../../common/types/kibana';
import { JobCreator } from './job_creator';
import {
Field,
@ -15,7 +15,7 @@ import {
} from '../../../../../../common/types/fields';
import { Job, Datafeed, Detector } from './configs';
import { createBasicDetector } from './util/default_configs';
import { JOB_TYPE, CREATED_BY_LABEL } from './util/constants';
import { JOB_TYPE, CREATED_BY_LABEL } from '../../../../../../common/constants/new_job';
import { getRichDetectors } from './util/general';
export class PopulationJobCreator extends JobCreator {
@ -25,7 +25,11 @@ export class PopulationJobCreator extends JobCreator {
private _byFields: SplitField[] = [];
protected _type: JOB_TYPE = JOB_TYPE.POPULATION;
constructor(indexPattern: IndexPattern, savedSearch: SavedSearch, query: object) {
constructor(
indexPattern: IndexPattern,
savedSearch: SavedSearchSavedObject | null,
query: object
) {
super(indexPattern, savedSearch, query);
this.createdBy = CREATED_BY_LABEL.POPULATION;
}

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { SavedSearch } from 'src/legacy/core_plugins/kibana/public/discover/types';
import { IndexPattern } from 'ui/index_patterns';
import { SavedSearchSavedObject } from '../../../../../../common/types/kibana';
import { parseInterval } from '../../../../../../common/util/parse_interval';
import { JobCreator } from './job_creator';
import { Field, Aggregation, AggFieldPair } from '../../../../../../common/types/fields';
@ -15,13 +15,17 @@ import {
ML_JOB_AGGREGATION,
ES_AGGREGATION,
} from '../../../../../../common/constants/aggregation_types';
import { JOB_TYPE, CREATED_BY_LABEL } from './util/constants';
import { JOB_TYPE, CREATED_BY_LABEL } from '../../../../../../common/constants/new_job';
import { getRichDetectors } from './util/general';
export class SingleMetricJobCreator extends JobCreator {
protected _type: JOB_TYPE = JOB_TYPE.SINGLE_METRIC;
constructor(indexPattern: IndexPattern, savedSearch: SavedSearch, query: object) {
constructor(
indexPattern: IndexPattern,
savedSearch: SavedSearchSavedObject | null,
query: object
) {
super(indexPattern, savedSearch, query);
this.createdBy = CREATED_BY_LABEL.SINGLE_METRIC;
}

View file

@ -8,7 +8,7 @@ import { SingleMetricJobCreator } from './single_metric_job_creator';
import { MultiMetricJobCreator } from './multi_metric_job_creator';
import { PopulationJobCreator } from './population_job_creator';
import { AdvancedJobCreator } from './advanced_job_creator';
import { JOB_TYPE } from './util/constants';
import { JOB_TYPE } from '../../../../../../common/constants/new_job';
export type JobCreatorType =
| SingleMetricJobCreator

View file

@ -19,7 +19,7 @@ import {
} from '../../../../../../../common/types/fields';
import { mlJobService } from '../../../../../services/job_service';
import { JobCreatorType, isMultiMetricJobCreator, isPopulationJobCreator } from '../index';
import { CREATED_BY_LABEL, JOB_TYPE } from './constants';
import { CREATED_BY_LABEL, JOB_TYPE } from '../../../../../../../common/constants/new_job';
const getFieldByIdFactory = (scriptFields: Field[]) => (id: string) => {
let field = newJobCapsService.getFieldById(id);

View file

@ -12,8 +12,8 @@ import { getSeverityType } from '../../../../../../common/util/anomaly_utils';
import { parseInterval } from '../../../../../../common/util/parse_interval';
import { ANOMALY_SEVERITY } from '../../../../../../common/constants/anomalies';
import { getScoresByRecord } from './searches';
import { JOB_TYPE } from '../job_creator/util/constants';
import { ChartLoader } from '../chart_loader';
import { JOB_TYPE } from '../../../../../../common/constants/new_job';
import { ES_AGGREGATION } from '../../../../../../common/constants/aggregation_types';
export interface Results {

View file

@ -1,14 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import './pages/new_job/route';
import './pages/new_job/directive';
import './pages/job_type/route';
import './pages/job_type/directive';
import './pages/index_or_search/route';
import './pages/index_or_search/directive';
import './recognize/route';
import './recognize/directive';

View file

@ -9,7 +9,7 @@ import { EuiFieldText } from '@elastic/eui';
import { JobCreatorContext } from '../../../job_creator_context';
import { Description } from './description';
import { useStringifiedValue } from '../hooks';
import { DEFAULT_QUERY_DELAY } from '../../../../../common/job_creator/util/constants';
import { DEFAULT_QUERY_DELAY } from '../../../../../../../../../common/constants/new_job';
export const QueryDelayInput: FC = () => {
const { jobCreator, jobCreatorUpdate, jobValidator, jobValidatorUpdated } = useContext(

View file

@ -17,7 +17,7 @@ import { ModelPlotSwitch } from './components/model_plot';
import { DedicatedIndexSwitch } from './components/dedicated_index';
import { ModelMemoryLimitInput } from '../../../common/model_memory_limit';
import { JobCreatorContext } from '../../../job_creator_context';
import { JOB_TYPE } from '../../../../../common/job_creator/util/constants';
import { JOB_TYPE } from '../../../../../../../../../common/constants/new_job';
const ButtonContent = i18n.translate(
'xpack.ml.newJob.wizard.jobDetailsStep.advancedSectionButton',

View file

@ -11,7 +11,7 @@ import { AggFieldPair, SplitField } from '../../../../../../../../../common/type
import { ChartSettings } from '../../../charts/common/settings';
import { LineChartData } from '../../../../../common/chart_loader';
import { ModelItem, Anomaly } from '../../../../../common/results_loader';
import { JOB_TYPE } from '../../../../../common/job_creator/util/constants';
import { JOB_TYPE } from '../../../../../../../../../common/constants/new_job';
import { SplitCards, useAnimateSplit } from '../split_cards';
import { DetectorTitle } from '../detector_title';
import { AnomalyChart, CHART_TYPE } from '../../../charts/anomaly_chart';

View file

@ -11,7 +11,7 @@ import { AggFieldPair, SplitField } from '../../../../../../../../../common/type
import { ChartSettings } from '../../../charts/common/settings';
import { LineChartData } from '../../../../../common/chart_loader';
import { ModelItem, Anomaly } from '../../../../../common/results_loader';
import { JOB_TYPE } from '../../../../../common/job_creator/util/constants';
import { JOB_TYPE } from '../../../../../../../../../common/constants/new_job';
import { SplitCards, useAnimateSplit } from '../split_cards';
import { DetectorTitle } from '../detector_title';
import { ByFieldSelector } from '../split_field';

View file

@ -9,7 +9,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiHorizontalRule, EuiSpacer } from '@elastic/eui';
import { SplitField } from '../../../../../../../../../common/types/fields';
import { JOB_TYPE } from '../../../../../common/job_creator/util/constants';
import { JOB_TYPE } from '../../../../../../../../../common/constants/new_job';
interface Props {
fieldValues: string[];

View file

@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiDescribedFormGroup, EuiFormRow } from '@elastic/eui';
import { JOB_TYPE } from '../../../../../common/job_creator/util/constants';
import { JOB_TYPE } from '../../../../../../../../../common/constants/new_job';
interface Props {
jobType: JOB_TYPE;

View file

@ -10,7 +10,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { JobCreatorContext } from '../job_creator_context';
import { WizardNav } from '../wizard_nav';
import { WIZARD_STEPS, StepProps } from '../step_types';
import { JOB_TYPE } from '../../../common/job_creator/util/constants';
import { JOB_TYPE } from '../../../../../../../common/constants/new_job';
import { SingleMetricView } from './components/single_metric_view';
import { MultiMetricView } from './components/multi_metric_view';
import { PopulationView } from './components/population_view';

View file

@ -10,7 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiDescriptionList, EuiFormRow } from '@elas
import { JobCreatorContext } from '../../../job_creator_context';
import { MLJobEditor } from '../../../../../../jobs_list/components/ml_job_editor';
import { calculateDatafeedFrequencyDefaultSeconds } from '../../../../../../../../../common/util/job_utils';
import { DEFAULT_QUERY_DELAY } from '../../../../../common/job_creator/util/constants';
import { DEFAULT_QUERY_DELAY } from '../../../../../../../../../common/constants/new_job';
import { getNewJobDefaults } from '../../../../../../../services/ml_server_info';
import { ListItems, defaultLabel, Italic } from '../common';

View file

@ -6,7 +6,7 @@
import React, { Fragment, FC, useContext } from 'react';
import { JobCreatorContext } from '../../../job_creator_context';
import { JOB_TYPE } from '../../../../../common/job_creator/util/constants';
import { JOB_TYPE } from '../../../../../../../../../common/constants/new_job';
import { SingleMetricView } from '../../../pick_fields_step/components/single_metric_view';
import { MultiMetricView } from '../../../pick_fields_step/components/multi_metric_view';
import { PopulationView } from '../../../pick_fields_step/components/population_view';

View file

@ -23,7 +23,7 @@ import { JobRunner } from '../../../common/job_runner';
import { mlJobService } from '../../../../../services/job_service';
import { JsonEditorFlyout, EDITOR_MODE } from '../common/json_editor_flyout';
import { DatafeedPreviewFlyout } from '../common/datafeed_preview_flyout';
import { JOB_TYPE } from '../../../common/job_creator/util/constants';
import { JOB_TYPE } from '../../../../../../../common/constants/new_job';
import { isSingleMetricJobCreator, isAdvancedJobCreator } from '../../../common/job_creator';
import { JobDetails } from './components/job_details';
import { DatafeedDetails } from './components/datafeed_details';

View file

@ -5,6 +5,8 @@
*/
import React, { FC, Fragment, useContext, useEffect, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { toastNotifications } from 'ui/notify';
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
import { timefilter } from 'ui/timefilter';
@ -16,7 +18,7 @@ import { useKibanaContext } from '../../../../../contexts/kibana';
import { FullTimeRangeSelector } from '../../../../../components/full_time_range_selector';
import { EventRateChart } from '../charts/event_rate_chart';
import { LineChartPoint } from '../../../common/chart_loader';
import { JOB_TYPE } from '../../../common/job_creator/util/constants';
import { JOB_TYPE } from '../../../../../../../common/constants/new_job';
import { GetTimeFieldRangeResponse } from '../../../../../services/ml_api_service';
import { TimeRangePicker, TimeRange } from '../../../common/components';
@ -78,10 +80,18 @@ export const TimeRangeStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep })
}, [jobCreatorUpdated]);
function fullTimeRangeCallback(range: GetTimeFieldRangeResponse) {
setTimeRange({
start: range.start.epoch,
end: range.end.epoch,
});
if (range.start.epoch !== null && range.end.epoch !== null) {
setTimeRange({
start: range.start.epoch,
end: range.end.epoch,
});
} else {
toastNotifications.addDanger(
i18n.translate('xpack.ml.newJob.wizard.timeRangeStep.fullTimeRangeError', {
defaultMessage: 'An error occurred obtaining the time range for the index',
})
);
}
}
return (

View file

@ -10,7 +10,7 @@ import { WIZARD_STEPS, StepProps } from '../step_types';
import { JobCreatorContext } from '../job_creator_context';
import { mlJobService } from '../../../../../services/job_service';
import { ValidateJob } from '../../../../../components/validate_job';
import { JOB_TYPE } from '../../../common/job_creator/util/constants';
import { JOB_TYPE } from '../../../../../../../common/constants/new_job';
const idFilterList = [
'job_id_valid',

View file

@ -1,45 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import ngMock from 'ng_mock';
import expect from '@kbn/expect';
import sinon from 'sinon';
// Import this way to be able to stub/mock functions later on in the tests using sinon.
import * as indexUtils from '../../../../../util/index_utils';
describe('ML - Index or Saved Search selection directive', () => {
let $scope;
let $compile;
let $element;
beforeEach(ngMock.module('kibana'));
beforeEach(() => {
ngMock.inject(function ($injector) {
$compile = $injector.get('$compile');
const $rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
});
});
afterEach(() => {
$scope.$destroy();
});
it('Initialize Index or Saved Search selection directive', done => {
sinon.stub(indexUtils, 'timeBasedIndexCheck').callsFake(() => false);
ngMock.inject(function () {
expect(() => {
$element = $compile('<ml-index-or-search />')($scope);
}).to.not.throwError();
// directive has scope: false
const scope = $element.isolateScope();
expect(scope).to.eql(undefined);
done();
});
});
});

View file

@ -1,42 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
// @ts-ignore
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
import { timefilter } from 'ui/timefilter';
import { I18nContext } from 'ui/i18n';
import { InjectorService } from '../../../../../../common/types/angular';
import { Page } from './page';
module.directive('mlIndexOrSearch', ($injector: InjectorService) => {
return {
scope: {},
restrict: 'E',
link: async (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
// remove time picker from top of page
timefilter.disableTimeRangeSelector();
timefilter.disableAutoRefreshSelector();
const $route = $injector.get<any>('$route');
const { nextStepPath } = $route.current.locals;
ReactDOM.render(
<I18nContext>{React.createElement(Page, { nextStepPath })}</I18nContext>,
element[0]
);
element.on('$destroy', () => {
ReactDOM.unmountComponentAtNode(element[0]);
scope.$destroy();
});
},
};
});

View file

@ -0,0 +1,8 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export { Page } from './page';
export { preConfiguredJobRedirect } from './preconfigured_job_redirect';

View file

@ -4,16 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { IndexPatternsContract } from '../../../../../../../../../../src/plugins/data/public';
import { mlJobService } from '../../../../services/job_service';
import { loadIndexPatterns, getIndexPatternIdFromName } from '../../../../util/index_utils';
import { CombinedJob } from '../../common/job_creator/configs';
import { CREATED_BY_LABEL, JOB_TYPE } from '../../common/job_creator/util/constants';
import { CREATED_BY_LABEL, JOB_TYPE } from '../../../../../../common/constants/new_job';
export async function preConfiguredJobRedirect() {
export async function preConfiguredJobRedirect(indexPatterns: IndexPatternsContract) {
const { job } = mlJobService.tempJobCloningObjects;
if (job) {
try {
await loadIndexPatterns();
await loadIndexPatterns(indexPatterns);
const redirectUrl = getWizardUrlFromCloningJob(job);
window.location.href = `#/${redirectUrl}`;
return Promise.reject();

View file

@ -1,47 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import uiRoutes from 'ui/routes';
import { checkMlNodesAvailable } from '../../../../ml_nodes_check';
import { preConfiguredJobRedirect } from './preconfigured_job_redirect';
import { checkLicenseExpired, checkBasicLicense } from '../../../../license/check_license';
import { loadIndexPatterns } from '../../../../util/index_utils';
import {
checkCreateJobsPrivilege,
checkFindFileStructurePrivilege,
} from '../../../../privilege/check_privilege';
import {
getCreateJobBreadcrumbs,
getDataVisualizerIndexOrSearchBreadcrumbs,
} from '../../../breadcrumbs';
uiRoutes.when('/jobs/new_job', {
redirectTo: '/jobs/new_job/step/index_or_search',
});
uiRoutes.when('/jobs/new_job/step/index_or_search', {
template: '<ml-index-or-search />',
k7Breadcrumbs: getCreateJobBreadcrumbs,
resolve: {
CheckLicense: checkLicenseExpired,
privileges: checkCreateJobsPrivilege,
indexPatterns: loadIndexPatterns,
preConfiguredJobRedirect,
checkMlNodesAvailable,
nextStepPath: () => '#/jobs/new_job/step/job_type',
},
});
uiRoutes.when('/datavisualizer_index_select', {
template: '<ml-index-or-search />',
k7Breadcrumbs: getDataVisualizerIndexOrSearchBreadcrumbs,
resolve: {
CheckLicense: checkBasicLicense,
privileges: checkFindFileStructurePrivilege,
indexPatterns: loadIndexPatterns,
nextStepPath: () => '#jobs/new_job/datavisualizer',
},
});

View file

@ -1,45 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import ngMock from 'ng_mock';
import expect from '@kbn/expect';
import sinon from 'sinon';
// Import this way to be able to stub/mock functions later on in the tests using sinon.
import * as indexUtils from '../../../../../util/index_utils';
describe('ML - Job Type Directive', () => {
let $scope;
let $compile;
let $element;
beforeEach(ngMock.module('kibana'));
beforeEach(() => {
ngMock.inject(function ($injector) {
$compile = $injector.get('$compile');
const $rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
});
});
afterEach(() => {
$scope.$destroy();
});
it('Initialize Job Type Directive', done => {
sinon.stub(indexUtils, 'timeBasedIndexCheck').callsFake(() => false);
ngMock.inject(function () {
expect(() => {
$element = $compile('<ml-job-type-page />')($scope);
}).to.not.throwError();
// directive has scope: false
const scope = $element.isolateScope();
expect(scope).to.eql(undefined);
done();
});
});
});

View file

@ -1,64 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
// @ts-ignore
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
import { timefilter } from 'ui/timefilter';
import { I18nContext } from 'ui/i18n';
import { InjectorService } from '../../../../../../common/types/angular';
import { createSearchItems } from '../../utils/new_job_utils';
import { Page } from './page';
import { KibanaContext, KibanaConfigTypeFix } from '../../../../contexts/kibana';
import { IndexPatternsContract } from '../../../../../../../../../../src/plugins/data/public';
module.directive('mlJobTypePage', ($injector: InjectorService) => {
return {
scope: {},
restrict: 'E',
link: async (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
// remove time picker from top of page
timefilter.disableTimeRangeSelector();
timefilter.disableAutoRefreshSelector();
const indexPatterns = $injector.get<IndexPatternsContract>('indexPatterns');
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
const $route = $injector.get<any>('$route');
const { indexPattern, savedSearch, combinedQuery } = createSearchItems(
kibanaConfig,
$route.current.locals.indexPattern,
$route.current.locals.savedSearch
);
const kibanaContext = {
combinedQuery,
currentIndexPattern: indexPattern,
currentSavedSearch: savedSearch,
indexPatterns,
kibanaConfig,
};
ReactDOM.render(
<I18nContext>
<KibanaContext.Provider value={kibanaContext}>
{React.createElement(Page)}
</KibanaContext.Provider>
</I18nContext>,
element[0]
);
element.on('$destroy', () => {
ReactDOM.unmountComponentAtNode(element[0]);
scope.$destroy();
});
},
};
});

View file

@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export { Page } from './page';

View file

@ -19,6 +19,7 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { useKibanaContext } from '../../../../contexts/kibana';
import { isSavedSearchSavedObject } from '../../../../../../common/types/kibana';
import { DataRecognizer } from '../../../../components/data_recognizer';
import { addItemToRecentlyAccessed } from '../../../../util/recently_accessed';
import { timeBasedIndexCheck } from '../../../../util/index_utils';
@ -32,33 +33,33 @@ export const Page: FC = () => {
const isTimeBasedIndex = timeBasedIndexCheck(currentIndexPattern);
const indexWarningTitle =
!isTimeBasedIndex && currentSavedSearch.id === undefined
? i18n.translate('xpack.ml.newJob.wizard.jobType.indexPatternNotTimeBasedMessage', {
defaultMessage: 'Index pattern {indexPatternTitle} is not time based',
values: { indexPatternTitle: currentIndexPattern.title },
})
: i18n.translate(
!isTimeBasedIndex && isSavedSearchSavedObject(currentSavedSearch)
? i18n.translate(
'xpack.ml.newJob.wizard.jobType.indexPatternFromSavedSearchNotTimeBasedMessage',
{
defaultMessage:
'{savedSearchTitle} uses index pattern {indexPatternTitle} which is not time based',
values: {
savedSearchTitle: currentSavedSearch.title,
savedSearchTitle: currentSavedSearch.attributes.title as string,
indexPatternTitle: currentIndexPattern.title,
},
}
);
const pageTitleLabel =
currentSavedSearch.id !== undefined
? i18n.translate('xpack.ml.newJob.wizard.jobType.savedSearchPageTitleLabel', {
defaultMessage: 'saved search {savedSearchTitle}',
values: { savedSearchTitle: currentSavedSearch.title },
})
: i18n.translate('xpack.ml.newJob.wizard.jobType.indexPatternPageTitleLabel', {
defaultMessage: 'index pattern {indexPatternTitle}',
)
: i18n.translate('xpack.ml.newJob.wizard.jobType.indexPatternNotTimeBasedMessage', {
defaultMessage: 'Index pattern {indexPatternTitle} is not time based',
values: { indexPatternTitle: currentIndexPattern.title },
});
const pageTitleLabel = isSavedSearchSavedObject(currentSavedSearch)
? i18n.translate('xpack.ml.newJob.wizard.jobType.savedSearchPageTitleLabel', {
defaultMessage: 'saved search {savedSearchTitle}',
values: { savedSearchTitle: currentSavedSearch.attributes.title as string },
})
: i18n.translate('xpack.ml.newJob.wizard.jobType.indexPatternPageTitleLabel', {
defaultMessage: 'index pattern {indexPatternTitle}',
values: { indexPatternTitle: currentIndexPattern.title },
});
const recognizerResults = {
count: 0,
onChange() {
@ -67,14 +68,15 @@ export const Page: FC = () => {
};
const getUrl = (basePath: string) => {
return currentSavedSearch.id === undefined
return !isSavedSearchSavedObject(currentSavedSearch)
? `${basePath}?index=${currentIndexPattern.id}`
: `${basePath}?savedSearchId=${currentSavedSearch.id}`;
};
const addSelectionToRecentlyAccessed = () => {
const title =
currentSavedSearch.id === undefined ? currentIndexPattern.title : currentSavedSearch.title;
const title = !isSavedSearchSavedObject(currentSavedSearch)
? currentIndexPattern.title
: (currentSavedSearch.attributes.title as string);
const url = getUrl('');
addItemToRecentlyAccessed('jobs/new_job/datavisualizer', title, url);

View file

@ -1,25 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import uiRoutes from 'ui/routes';
import { checkMlNodesAvailable } from '../../../../ml_nodes_check';
import { checkLicenseExpired } from '../../../../license/check_license';
import { checkCreateJobsPrivilege } from '../../../../privilege/check_privilege';
import { loadCurrentIndexPattern, loadCurrentSavedSearch } from '../../../../util/index_utils';
import { getCreateJobBreadcrumbs } from '../../../breadcrumbs';
uiRoutes.when('/jobs/new_job/step/job_type', {
template: '<ml-job-type-page />',
k7Breadcrumbs: getCreateJobBreadcrumbs,
resolve: {
CheckLicense: checkLicenseExpired,
privileges: checkCreateJobsPrivilege,
indexPattern: loadCurrentIndexPattern,
savedSearch: loadCurrentSavedSearch,
checkMlNodesAvailable,
},
});

View file

@ -1,76 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
// @ts-ignore
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
import { timefilter } from 'ui/timefilter';
import { I18nContext } from 'ui/i18n';
import { IndexPatternsContract } from '../../../../../../../../../../src/plugins/data/public';
import { InjectorService } from '../../../../../../common/types/angular';
import { createSearchItems } from '../../utils/new_job_utils';
import { Page, PageProps } from './page';
import { JOB_TYPE } from '../../common/job_creator/util/constants';
import { KibanaContext, KibanaConfigTypeFix } from '../../../../contexts/kibana';
module.directive('mlNewJobPage', ($injector: InjectorService) => {
return {
scope: {},
restrict: 'E',
link: async (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
timefilter.disableTimeRangeSelector();
timefilter.disableAutoRefreshSelector();
const indexPatterns = $injector.get<IndexPatternsContract>('indexPatterns');
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
const $route = $injector.get<any>('$route');
const existingJobsAndGroups = $route.current.locals.existingJobsAndGroups;
if ($route.current.locals.jobType === undefined) {
return;
}
const jobType: JOB_TYPE = $route.current.locals.jobType;
const { indexPattern, savedSearch, combinedQuery } = createSearchItems(
kibanaConfig,
$route.current.locals.indexPattern,
$route.current.locals.savedSearch
);
const kibanaContext = {
combinedQuery,
currentIndexPattern: indexPattern,
currentSavedSearch: savedSearch,
indexPatterns,
kibanaConfig,
};
const props: PageProps = {
existingJobsAndGroups,
jobType,
};
ReactDOM.render(
<I18nContext>
<KibanaContext.Provider value={kibanaContext}>
{React.createElement(Page, props)}
</KibanaContext.Provider>
</I18nContext>,
element[0]
);
element.on('$destroy', () => {
ReactDOM.unmountComponentAtNode(element[0]);
scope.$destroy();
});
},
};
});

View file

@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export { Page } from './page';

View file

@ -16,7 +16,7 @@ import {
JOB_TYPE,
DEFAULT_MODEL_MEMORY_LIMIT,
DEFAULT_BUCKET_SPAN,
} from '../../common/job_creator/util/constants';
} from '../../../../../../common/constants/new_job';
import { ChartLoader } from '../../common/chart_loader';
import { ResultsLoader } from '../../common/results_loader';
import { JobValidator } from '../../common/job_validator';
@ -104,7 +104,7 @@ export const Page: FC<PageProps> = ({ existingJobsAndGroups, jobType }) => {
jobCreator.modelPlot = true;
}
if (kibanaContext.currentSavedSearch.id !== undefined) {
if (kibanaContext.currentSavedSearch !== null) {
// Jobs created from saved searches cannot be cloned in the wizard as the
// ML job config holds no reference to the saved search ID.
jobCreator.createdBy = null;

View file

@ -1,65 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import uiRoutes from 'ui/routes';
import { checkFullLicense } from '../../../../license/check_license';
import { checkGetJobsPrivilege } from '../../../../privilege/check_privilege';
import { loadCurrentIndexPattern, loadCurrentSavedSearch } from '../../../../util/index_utils';
import {
getCreateSingleMetricJobBreadcrumbs,
getCreateMultiMetricJobBreadcrumbs,
getCreatePopulationJobBreadcrumbs,
getAdvancedJobConfigurationBreadcrumbs,
} from '../../../breadcrumbs';
import { Route } from '../../../../../../common/types/kibana';
import { loadNewJobCapabilities } from '../../../../services/new_job_capabilities_service';
import { loadMlServerInfo } from '../../../../services/ml_server_info';
import { mlJobService } from '../../../../services/job_service';
import { JOB_TYPE } from '../../common/job_creator/util/constants';
const template = `<ml-new-job-page />`;
const routes: Route[] = [
{
id: JOB_TYPE.SINGLE_METRIC,
k7Breadcrumbs: getCreateSingleMetricJobBreadcrumbs,
},
{
id: JOB_TYPE.MULTI_METRIC,
k7Breadcrumbs: getCreateMultiMetricJobBreadcrumbs,
},
{
id: JOB_TYPE.POPULATION,
k7Breadcrumbs: getCreatePopulationJobBreadcrumbs,
},
{
id: JOB_TYPE.ADVANCED,
k7Breadcrumbs: getAdvancedJobConfigurationBreadcrumbs,
},
];
routes.forEach((route: Route) => {
uiRoutes.when(`/jobs/new_job/${route.id}`, {
template,
k7Breadcrumbs: route.k7Breadcrumbs,
resolve: {
CheckLicense: checkFullLicense,
privileges: checkGetJobsPrivilege,
indexPattern: loadCurrentIndexPattern,
savedSearch: loadCurrentSavedSearch,
loadNewJobCapabilities,
loadMlServerInfo,
existingJobsAndGroups: mlJobService.getJobAndGroupIds,
jobType: () => route.id,
},
});
});

View file

@ -20,7 +20,7 @@ import { JobValidator } from '../../common/job_validator';
import { newJobCapsService } from '../../../../services/new_job_capabilities_service';
import { WizardSteps } from './wizard_steps';
import { WizardHorizontalSteps } from './wizard_horizontal_steps';
import { JOB_TYPE } from '../../common/job_creator/util/constants';
import { JOB_TYPE } from '../../../../../../common/constants/new_job';
interface Props {
jobCreator: JobCreatorType;

View file

@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
import { EuiStepsHorizontal } from '@elastic/eui';
import { WIZARD_STEPS } from '../components/step_types';
import { JOB_TYPE } from '../../common/job_creator/util/constants';
import { JOB_TYPE } from '../../../../../../common/constants/new_job';
interface Props {
currentStep: WIZARD_STEPS;

View file

@ -34,10 +34,10 @@ export const WizardSteps: FC<Props> = ({ currentStep, setCurrentStep }) => {
const [additionalExpanded, setAdditionalExpanded] = useState(false);
function getSummaryStepTitle() {
if (kibanaContext.currentSavedSearch.id !== undefined) {
if (kibanaContext.currentSavedSearch !== null) {
return i18n.translate('xpack.ml.newJob.wizard.stepComponentWrapper.summaryTitleSavedSearch', {
defaultMessage: 'New job from saved search {title}',
values: { title: kibanaContext.currentSavedSearch.title },
values: { title: kibanaContext.currentSavedSearch.attributes.title as string },
});
} else if (kibanaContext.currentIndexPattern.id !== undefined) {
return i18n.translate(

View file

@ -1,45 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import ngMock from 'ng_mock';
import expect from '@kbn/expect';
import sinon from 'sinon';
// Import this way to be able to stub/mock functions later on in the tests using sinon.
import * as indexUtils from '../../../../util/index_utils';
describe('ML - Recognize job directive', () => {
let $scope;
let $compile;
let $element;
beforeEach(ngMock.module('kibana'));
beforeEach(() => {
ngMock.inject(function ($injector) {
$compile = $injector.get('$compile');
const $rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
});
});
afterEach(() => {
$scope.$destroy();
});
it('Initialize Recognize job directive', done => {
sinon.stub(indexUtils, 'timeBasedIndexCheck').callsFake(() => false);
ngMock.inject(function () {
expect(() => {
$element = $compile('<ml-recognize-page />')($scope);
}).to.not.throwError();
// directive has scope: false
const scope = $element.isolateScope();
expect(scope).to.eql(undefined);
done();
});
});
});

View file

@ -1,68 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
// @ts-ignore
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
import { timefilter } from 'ui/timefilter';
import { I18nContext } from 'ui/i18n';
import { InjectorService } from '../../../../../common/types/angular';
import { createSearchItems } from '../utils/new_job_utils';
import { Page } from './page';
import { KibanaContext, KibanaConfigTypeFix } from '../../../contexts/kibana';
import { IndexPatternsContract } from '../../../../../../../../../src/plugins/data/public';
module.directive('mlRecognizePage', ($injector: InjectorService) => {
return {
scope: {},
restrict: 'E',
link: async (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
// remove time picker from top of page
timefilter.disableTimeRangeSelector();
timefilter.disableAutoRefreshSelector();
const indexPatterns = $injector.get<IndexPatternsContract>('indexPatterns');
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
const $route = $injector.get<any>('$route');
const moduleId = $route.current.params.id;
const existingGroupIds: string[] = $route.current.locals.existingJobsAndGroups.groupIds;
const { indexPattern, savedSearch, combinedQuery } = createSearchItems(
kibanaConfig,
$route.current.locals.indexPattern,
$route.current.locals.savedSearch
);
const kibanaContext = {
combinedQuery,
currentIndexPattern: indexPattern,
currentSavedSearch: savedSearch,
indexPatterns,
kibanaConfig,
};
ReactDOM.render(
<I18nContext>
<KibanaContext.Provider value={kibanaContext}>
{React.createElement(Page, { moduleId, existingGroupIds })}
</KibanaContext.Provider>
</I18nContext>,
element[0]
);
element.on('$destroy', () => {
ReactDOM.unmountComponentAtNode(element[0]);
scope.$destroy();
});
},
};
});

View file

@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export { Page } from './page';

Some files were not shown because too many files have changed in this diff Show more