mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
[transform] Move ML "Data Frame Transforms" to Kibana management section "Transforms". (#45880)
Moves "Data frame transforms" from the ML plugin to its own "transform" plugin within the Kibana management section.
This commit is contained in:
parent
4f9b17cd8e
commit
bcf9ec3662
283 changed files with 4741 additions and 3697 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -35,6 +35,7 @@
|
||||||
/x-pack/test/functional/apps/machine_learning/ @elastic/ml-ui
|
/x-pack/test/functional/apps/machine_learning/ @elastic/ml-ui
|
||||||
/x-pack/test/functional/services/machine_learning/ @elastic/ml-ui
|
/x-pack/test/functional/services/machine_learning/ @elastic/ml-ui
|
||||||
/x-pack/test/functional/services/ml.ts @elastic/ml-ui
|
/x-pack/test/functional/services/ml.ts @elastic/ml-ui
|
||||||
|
/x-pack/legacy/plugins/transform/ @elastic/ml-ui
|
||||||
|
|
||||||
# Operations
|
# Operations
|
||||||
/renovate.json5 @elastic/kibana-operations
|
/renovate.json5 @elastic/kibana-operations
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"xpack.server": "legacy/server",
|
"xpack.server": "legacy/server",
|
||||||
"xpack.snapshotRestore": "legacy/plugins/snapshot_restore",
|
"xpack.snapshotRestore": "legacy/plugins/snapshot_restore",
|
||||||
"xpack.spaces": "legacy/plugins/spaces",
|
"xpack.spaces": "legacy/plugins/spaces",
|
||||||
|
"xpack.transform": "legacy/plugins/transform",
|
||||||
"xpack.upgradeAssistant": "legacy/plugins/upgrade_assistant",
|
"xpack.upgradeAssistant": "legacy/plugins/upgrade_assistant",
|
||||||
"xpack.uptime": "legacy/plugins/uptime",
|
"xpack.uptime": "legacy/plugins/uptime",
|
||||||
"xpack.watcher": "legacy/plugins/watcher"
|
"xpack.watcher": "legacy/plugins/watcher"
|
||||||
|
|
|
@ -41,6 +41,7 @@ import { fileUpload } from './legacy/plugins/file_upload';
|
||||||
import { telemetry } from './legacy/plugins/telemetry';
|
import { telemetry } from './legacy/plugins/telemetry';
|
||||||
import { encryptedSavedObjects } from './legacy/plugins/encrypted_saved_objects';
|
import { encryptedSavedObjects } from './legacy/plugins/encrypted_saved_objects';
|
||||||
import { snapshotRestore } from './legacy/plugins/snapshot_restore';
|
import { snapshotRestore } from './legacy/plugins/snapshot_restore';
|
||||||
|
import { transform } from './legacy/plugins/transform';
|
||||||
import { actions } from './legacy/plugins/actions';
|
import { actions } from './legacy/plugins/actions';
|
||||||
import { alerting } from './legacy/plugins/alerting';
|
import { alerting } from './legacy/plugins/alerting';
|
||||||
import { lens } from './legacy/plugins/lens';
|
import { lens } from './legacy/plugins/lens';
|
||||||
|
@ -75,6 +76,7 @@ module.exports = function (kibana) {
|
||||||
infra(kibana),
|
infra(kibana),
|
||||||
taskManager(kibana),
|
taskManager(kibana),
|
||||||
rollup(kibana),
|
rollup(kibana),
|
||||||
|
transform(kibana),
|
||||||
siem(kibana),
|
siem(kibana),
|
||||||
remoteClusters(kibana),
|
remoteClusters(kibana),
|
||||||
crossClusterReplication(kibana),
|
crossClusterReplication(kibana),
|
||||||
|
|
|
@ -16,10 +16,6 @@ export interface AnalyticsMessage extends AuditMessageBase {
|
||||||
analytics_id: string;
|
analytics_id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransformMessage extends AuditMessageBase {
|
|
||||||
transform_id: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface JobMessage extends AuditMessageBase {
|
export interface JobMessage extends AuditMessageBase {
|
||||||
job_id: string;
|
job_id: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,12 +28,6 @@ export interface Privileges {
|
||||||
canDeleteFilter: boolean;
|
canDeleteFilter: boolean;
|
||||||
// File Data Visualizer
|
// File Data Visualizer
|
||||||
canFindFileStructure: boolean;
|
canFindFileStructure: boolean;
|
||||||
// Data Frame Transforms
|
|
||||||
canGetDataFrame: boolean;
|
|
||||||
canDeleteDataFrame: boolean;
|
|
||||||
canPreviewDataFrame: boolean;
|
|
||||||
canCreateDataFrame: boolean;
|
|
||||||
canStartStopDataFrame: boolean;
|
|
||||||
// Data Frame Analytics
|
// Data Frame Analytics
|
||||||
canGetDataFrameAnalytics: boolean;
|
canGetDataFrameAnalytics: boolean;
|
||||||
canDeleteDataFrameAnalytics: boolean;
|
canDeleteDataFrameAnalytics: boolean;
|
||||||
|
@ -65,12 +59,6 @@ export function getDefaultPrivileges(): Privileges {
|
||||||
canDeleteFilter: false,
|
canDeleteFilter: false,
|
||||||
// File Data Visualizer
|
// File Data Visualizer
|
||||||
canFindFileStructure: false,
|
canFindFileStructure: false,
|
||||||
// Data Frame Transforms
|
|
||||||
canGetDataFrame: false,
|
|
||||||
canDeleteDataFrame: false,
|
|
||||||
canPreviewDataFrame: false,
|
|
||||||
canCreateDataFrame: false,
|
|
||||||
canStartStopDataFrame: false,
|
|
||||||
// Data Frame Analytics
|
// Data Frame Analytics
|
||||||
canGetDataFrameAnalytics: false,
|
canGetDataFrameAnalytics: false,
|
||||||
canDeleteDataFrameAnalytics: false,
|
canDeleteDataFrameAnalytics: false,
|
||||||
|
|
|
@ -224,7 +224,7 @@ export function mlFunctionToESAggregation(functionName) {
|
||||||
// Job name must contain lowercase alphanumeric (a-z and 0-9), hyphens or underscores;
|
// Job name must contain lowercase alphanumeric (a-z and 0-9), hyphens or underscores;
|
||||||
// it must also start and end with an alphanumeric character'
|
// it must also start and end with an alphanumeric character'
|
||||||
export function isJobIdValid(jobId) {
|
export function isJobIdValid(jobId) {
|
||||||
return (jobId.match(/^[a-z0-9\-\_]+$/g) && !jobId.match(/^([_-].*)?(.*[_-])?$/g)) ? true : false;
|
return /^[a-z0-9\-\_]+$/g.test(jobId) && !/^([_-].*)?(.*[_-])?$/g.test(jobId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// To get median data for jobs and charts we need to use Elasticsearch's
|
// To get median data for jobs and charts we need to use Elasticsearch's
|
||||||
|
|
|
@ -20,7 +20,6 @@ import 'plugins/ml/jobs';
|
||||||
import 'plugins/ml/overview';
|
import 'plugins/ml/overview';
|
||||||
import 'plugins/ml/services/calendar_service';
|
import 'plugins/ml/services/calendar_service';
|
||||||
import 'plugins/ml/components/messagebar';
|
import 'plugins/ml/components/messagebar';
|
||||||
import 'plugins/ml/data_frame';
|
|
||||||
import 'plugins/ml/data_frame_analytics';
|
import 'plugins/ml/data_frame_analytics';
|
||||||
import 'plugins/ml/datavisualizer';
|
import 'plugins/ml/datavisualizer';
|
||||||
import 'plugins/ml/explorer';
|
import 'plugins/ml/explorer';
|
||||||
|
|
|
@ -17,7 +17,10 @@ interface Props {
|
||||||
const [INFO, WARNING, ERROR] = ['info', 'warning', 'error'];
|
const [INFO, WARNING, ERROR] = ['info', 'warning', 'error'];
|
||||||
|
|
||||||
export const JobIcon: FC<Props> = ({ message, showTooltip = false }) => {
|
export const JobIcon: FC<Props> = ({ message, showTooltip = false }) => {
|
||||||
if (message !== undefined) {
|
if (message === undefined) {
|
||||||
|
return <span />;
|
||||||
|
}
|
||||||
|
|
||||||
let color = 'primary';
|
let color = 'primary';
|
||||||
const icon = 'alert';
|
const icon = 'alert';
|
||||||
|
|
||||||
|
@ -38,7 +41,4 @@ export const JobIcon: FC<Props> = ({ message, showTooltip = false }) => {
|
||||||
} else {
|
} else {
|
||||||
return <EuiIcon type={icon} color={color} />;
|
return <EuiIcon type={icon} color={color} />;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return <span />;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,13 +37,6 @@ function getTabs(disableLinks: boolean): Tab[] {
|
||||||
}),
|
}),
|
||||||
disabled: disableLinks,
|
disabled: disableLinks,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'data_frames',
|
|
||||||
name: i18n.translate('xpack.ml.navMenu.dataFrameTabLinkText', {
|
|
||||||
defaultMessage: 'Transforms',
|
|
||||||
}),
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'data_frame_analytics',
|
id: 'data_frame_analytics',
|
||||||
name: i18n.translate('xpack.ml.navMenu.dataFrameAnalyticsTabLinkText', {
|
name: i18n.translate('xpack.ml.navMenu.dataFrameAnalyticsTabLinkText', {
|
||||||
|
@ -68,7 +61,6 @@ interface TabData {
|
||||||
const TAB_DATA: Record<TabId, TabData> = {
|
const TAB_DATA: Record<TabId, TabData> = {
|
||||||
overview: { testSubject: 'mlMainTab overview', pathId: 'overview' },
|
overview: { testSubject: 'mlMainTab overview', pathId: 'overview' },
|
||||||
anomaly_detection: { testSubject: 'mlMainTab anomalyDetection', pathId: 'jobs' },
|
anomaly_detection: { testSubject: 'mlMainTab anomalyDetection', pathId: 'jobs' },
|
||||||
data_frames: { testSubject: 'mlMainTab dataFrames' },
|
|
||||||
data_frame_analytics: { testSubject: 'mlMainTab dataFrameAnalytics' },
|
data_frame_analytics: { testSubject: 'mlMainTab dataFrameAnalytics' },
|
||||||
datavisualizer: { testSubject: 'mlMainTab dataVisualizer' },
|
datavisualizer: { testSubject: 'mlMainTab dataVisualizer' },
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,6 @@ const tabSupport: TabSupport = {
|
||||||
overview: null,
|
overview: null,
|
||||||
jobs: 'anomaly_detection',
|
jobs: 'anomaly_detection',
|
||||||
settings: 'anomaly_detection',
|
settings: 'anomaly_detection',
|
||||||
data_frames: null,
|
|
||||||
data_frame_analytics: null,
|
data_frame_analytics: null,
|
||||||
datavisualizer: null,
|
datavisualizer: null,
|
||||||
filedatavisualizer: null,
|
filedatavisualizer: null,
|
||||||
|
|
|
@ -21,7 +21,6 @@ export function getTabs(tabId: TabId, disableLinks: boolean): Tab[] {
|
||||||
const TAB_MAP: Partial<Record<TabId, Tab[]>> = {
|
const TAB_MAP: Partial<Record<TabId, Tab[]>> = {
|
||||||
overview: [],
|
overview: [],
|
||||||
datavisualizer: [],
|
datavisualizer: [],
|
||||||
data_frames: [],
|
|
||||||
data_frame_analytics: [],
|
data_frame_analytics: [],
|
||||||
anomaly_detection: [
|
anomaly_detection: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
.stat {
|
.stat {
|
||||||
margin-right: $euiSizeS;
|
margin-right: $euiSizeS;
|
||||||
|
|
||||||
.stat-value {
|
|
||||||
font-weight: bold
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,4 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {
|
export { StatsBar, AnalyticStatsBarStats, JobStatsBarStats } from './stats_bar';
|
||||||
StatsBar,
|
|
||||||
TransformStatsBarStats,
|
|
||||||
AnalyticStatsBarStats,
|
|
||||||
JobStatsBarStats,
|
|
||||||
} from './stats_bar';
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ interface StatProps {
|
||||||
export const Stat: FC<StatProps> = ({ stat }) => {
|
export const Stat: FC<StatProps> = ({ stat }) => {
|
||||||
return (
|
return (
|
||||||
<span className="stat">
|
<span className="stat">
|
||||||
<span>{stat.label}</span>: <span className="stat-value">{stat.value}</span>
|
<span>{stat.label}</span>: <strong>{stat.value}</strong>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,18 +18,12 @@ export interface JobStatsBarStats extends Stats {
|
||||||
activeDatafeeds: StatsBarStat;
|
activeDatafeeds: StatsBarStat;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransformStatsBarStats extends Stats {
|
|
||||||
batch: StatsBarStat;
|
|
||||||
continuous: StatsBarStat;
|
|
||||||
started: StatsBarStat;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AnalyticStatsBarStats extends Stats {
|
export interface AnalyticStatsBarStats extends Stats {
|
||||||
started: StatsBarStat;
|
started: StatsBarStat;
|
||||||
stopped: StatsBarStat;
|
stopped: StatsBarStat;
|
||||||
}
|
}
|
||||||
|
|
||||||
type StatsBarStats = TransformStatsBarStats | JobStatsBarStats | AnalyticStatsBarStats;
|
type StatsBarStats = JobStatsBarStats | AnalyticStatsBarStats;
|
||||||
type StatsKey = keyof StatsBarStats;
|
type StatsKey = keyof StatsBarStats;
|
||||||
|
|
||||||
interface StatsBarProps {
|
interface StatsBarProps {
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
@import 'pages/data_frame_new_pivot/components/aggregation_list/index';
|
|
||||||
@import 'pages/data_frame_new_pivot/components/group_by_list/index';
|
|
||||||
@import 'pages/transform_management/components/transform_list/index';
|
|
|
@ -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 { i18n } from '@kbn/i18n';
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import { ML_BREADCRUMB } from '../breadcrumbs';
|
|
||||||
|
|
||||||
export function getJobManagementBreadcrumbs() {
|
|
||||||
// Whilst top level nav menu with tabs remains,
|
|
||||||
// use root ML breadcrumb.
|
|
||||||
return [ML_BREADCRUMB];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDataFrameBreadcrumbs() {
|
|
||||||
return [
|
|
||||||
ML_BREADCRUMB,
|
|
||||||
{
|
|
||||||
text: i18n.translate('xpack.ml.dataFrameBreadcrumbs.dataFrameLabel', {
|
|
||||||
defaultMessage: 'Transforms',
|
|
||||||
}),
|
|
||||||
href: '',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
const DATA_FRAME_HOME = {
|
|
||||||
text: i18n.translate('xpack.ml.dataFrameBreadcrumbs.dataFrameLabel', {
|
|
||||||
defaultMessage: 'Transforms',
|
|
||||||
}),
|
|
||||||
href: '#/data_frames',
|
|
||||||
};
|
|
||||||
|
|
||||||
export function getDataFrameCreateBreadcrumbs() {
|
|
||||||
return [
|
|
||||||
ML_BREADCRUMB,
|
|
||||||
DATA_FRAME_HOME,
|
|
||||||
{
|
|
||||||
text: i18n.translate('xpack.ml.dataFrameBreadcrumbs.dataFrameCreateLabel', {
|
|
||||||
defaultMessage: 'Create transform',
|
|
||||||
}),
|
|
||||||
href: '',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDataFrameIndexOrSearchBreadcrumbs() {
|
|
||||||
return [
|
|
||||||
ML_BREADCRUMB,
|
|
||||||
DATA_FRAME_HOME,
|
|
||||||
{
|
|
||||||
text: i18n.translate('xpack.ml.dataFrameBreadcrumbs.selectIndexOrSearchLabel', {
|
|
||||||
defaultMessage: 'Select index or search',
|
|
||||||
}),
|
|
||||||
href: '',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
|
@ -1,12 +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/access_denied/directive';
|
|
||||||
import './pages/access_denied/route';
|
|
||||||
import './pages/transform_management/directive';
|
|
||||||
import './pages/transform_management/route';
|
|
||||||
import './pages/data_frame_new_pivot/directive';
|
|
||||||
import './pages/data_frame_new_pivot/route';
|
|
|
@ -1,50 +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';
|
|
||||||
import uiChrome from 'ui/chrome';
|
|
||||||
|
|
||||||
const module = uiModules.get('apps/ml', ['react']);
|
|
||||||
|
|
||||||
import { I18nContext } from 'ui/i18n';
|
|
||||||
import { InjectorService } from '../../../../common/types/angular';
|
|
||||||
|
|
||||||
import { Page } from './page';
|
|
||||||
|
|
||||||
module.directive('mlDataFrameAccessDenied', ($injector: InjectorService) => {
|
|
||||||
return {
|
|
||||||
scope: {},
|
|
||||||
restrict: 'E',
|
|
||||||
link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
|
|
||||||
const kbnBaseUrl = $injector.get<string>('kbnBaseUrl');
|
|
||||||
const kbnUrl = $injector.get<any>('kbnUrl');
|
|
||||||
|
|
||||||
const goToKibana = () => {
|
|
||||||
window.location.href = uiChrome.getBasePath() + kbnBaseUrl;
|
|
||||||
};
|
|
||||||
|
|
||||||
const retry = () => {
|
|
||||||
kbnUrl.redirect('/data_frames');
|
|
||||||
};
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<I18nContext>
|
|
||||||
<Page goToKibana={goToKibana} retry={retry} />
|
|
||||||
</I18nContext>,
|
|
||||||
element[0]
|
|
||||||
);
|
|
||||||
|
|
||||||
element.on('$destroy', () => {
|
|
||||||
ReactDOM.unmountComponentAtNode(element[0]);
|
|
||||||
scope.$destroy();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -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 { render, fireEvent, cleanup } from 'react-testing-library';
|
|
||||||
|
|
||||||
import { I18nProvider } from '@kbn/i18n/react';
|
|
||||||
|
|
||||||
import { Page } from './page';
|
|
||||||
|
|
||||||
jest.mock('../../../components/navigation_menu/navigation_menu', () => ({
|
|
||||||
NavigationMenu: () => <div id="mockNavigationMenu" />,
|
|
||||||
}));
|
|
||||||
|
|
||||||
afterEach(cleanup);
|
|
||||||
|
|
||||||
describe('Data Frame: Access denied <Page />', () => {
|
|
||||||
test('Minimal initialization', () => {
|
|
||||||
const props = {
|
|
||||||
goToKibana: jest.fn(),
|
|
||||||
retry: jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const tree = (
|
|
||||||
<I18nProvider>
|
|
||||||
<Page {...props} />
|
|
||||||
</I18nProvider>
|
|
||||||
);
|
|
||||||
|
|
||||||
const { getByText } = render(tree);
|
|
||||||
|
|
||||||
fireEvent.click(getByText(/Back to Kibana home/i));
|
|
||||||
fireEvent.click(getByText(/Retry/i));
|
|
||||||
|
|
||||||
expect(props.goToKibana).toHaveBeenCalledTimes(1);
|
|
||||||
expect(props.retry).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,98 +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, { FC, Fragment } from 'react';
|
|
||||||
|
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
|
||||||
import { i18n } from '@kbn/i18n';
|
|
||||||
|
|
||||||
import {
|
|
||||||
EuiButton,
|
|
||||||
EuiCallOut,
|
|
||||||
EuiFlexGroup,
|
|
||||||
EuiFlexItem,
|
|
||||||
EuiPage,
|
|
||||||
EuiPageBody,
|
|
||||||
EuiPageContentBody,
|
|
||||||
EuiPageContentHeader,
|
|
||||||
EuiPageContentHeaderSection,
|
|
||||||
EuiSpacer,
|
|
||||||
EuiText,
|
|
||||||
EuiTitle,
|
|
||||||
} from '@elastic/eui';
|
|
||||||
|
|
||||||
import { NavigationMenu } from '../../../components/navigation_menu/navigation_menu';
|
|
||||||
|
|
||||||
interface PageProps {
|
|
||||||
goToKibana: () => void;
|
|
||||||
retry: () => void;
|
|
||||||
}
|
|
||||||
export const Page: FC<PageProps> = ({ goToKibana, retry }) => (
|
|
||||||
<Fragment>
|
|
||||||
<NavigationMenu tabId="access-denied" />
|
|
||||||
<EuiPage>
|
|
||||||
<EuiPageBody>
|
|
||||||
<EuiPageContentHeader>
|
|
||||||
<EuiPageContentHeaderSection>
|
|
||||||
<EuiTitle>
|
|
||||||
<h1>
|
|
||||||
<FormattedMessage
|
|
||||||
id="xpack.ml.dataframe.accessDeniedTitle"
|
|
||||||
defaultMessage="Access denied"
|
|
||||||
/>
|
|
||||||
</h1>
|
|
||||||
</EuiTitle>
|
|
||||||
</EuiPageContentHeaderSection>
|
|
||||||
</EuiPageContentHeader>
|
|
||||||
<EuiPageContentBody>
|
|
||||||
<EuiSpacer size="m" />
|
|
||||||
<EuiCallOut
|
|
||||||
title={i18n.translate('xpack.ml.dataframe.noPermissionToAccessMLLabel', {
|
|
||||||
defaultMessage: 'You need permission to access Data Frames',
|
|
||||||
})}
|
|
||||||
color="danger"
|
|
||||||
iconType="cross"
|
|
||||||
>
|
|
||||||
<EuiText size="s">
|
|
||||||
<p>
|
|
||||||
<FormattedMessage
|
|
||||||
id="xpack.ml.dataframe.noGrantedPrivilegesDescription"
|
|
||||||
defaultMessage="You must have the privileges granted in the {kibanaUserParam} and {dataFrameUserParam} roles.{br}Your system admin can set these roles on the Management User page."
|
|
||||||
values={{
|
|
||||||
kibanaUserParam: <span className="text-monospace">kibana_user</span>,
|
|
||||||
dataFrameUserParam: (
|
|
||||||
<span className="text-monospace">data_frame_transforms_user</span>
|
|
||||||
),
|
|
||||||
br: <br />,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</EuiText>
|
|
||||||
</EuiCallOut>
|
|
||||||
<EuiSpacer size="m" />
|
|
||||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
|
||||||
<EuiFlexItem grow={false}>
|
|
||||||
<EuiButton fill onClick={goToKibana} size="s">
|
|
||||||
<FormattedMessage
|
|
||||||
id="xpack.ml.dataframe.accessDenied.backToKibanaHomeButtonLabel"
|
|
||||||
defaultMessage="Back to Kibana home"
|
|
||||||
/>
|
|
||||||
</EuiButton>
|
|
||||||
</EuiFlexItem>
|
|
||||||
<EuiFlexItem grow={false}>
|
|
||||||
<EuiButton fill onClick={retry} size="s">
|
|
||||||
<FormattedMessage
|
|
||||||
id="xpack.ml.dataframe.accessDenied.retryButtonLabel"
|
|
||||||
defaultMessage="Retry"
|
|
||||||
/>
|
|
||||||
</EuiButton>
|
|
||||||
</EuiFlexItem>
|
|
||||||
</EuiFlexGroup>
|
|
||||||
</EuiPageContentBody>
|
|
||||||
</EuiPageBody>
|
|
||||||
</EuiPage>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
|
@ -1,17 +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';
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import { getDataFrameBreadcrumbs } from '../../breadcrumbs';
|
|
||||||
|
|
||||||
const template = `<ml-data-frame-access-denied />`;
|
|
||||||
|
|
||||||
uiRoutes.when('/data_frames/access-denied', {
|
|
||||||
template,
|
|
||||||
k7Breadcrumbs: getDataFrameBreadcrumbs,
|
|
||||||
});
|
|
|
@ -1,7 +0,0 @@
|
||||||
.mlAggregationLabel--button {
|
|
||||||
width: $euiSizeL;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mlAggregationLabel--text {
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
.mlGroupByLabel--button {
|
|
||||||
width: $euiSizeL;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mlGroupByLabel--text {
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mlGroupByLabel--interval {
|
|
||||||
max-width: 25%;
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Data Frame: <SourceIndexPreview /> Minimal initialization 1`] = `
|
|
||||||
<div>
|
|
||||||
<ContextProvider
|
|
||||||
value={
|
|
||||||
Object {
|
|
||||||
"combinedQuery": Object {
|
|
||||||
"language": "the-query-language",
|
|
||||||
"query": "the-query-string",
|
|
||||||
},
|
|
||||||
"currentIndexPattern": Object {
|
|
||||||
"fields": Array [],
|
|
||||||
"id": "the-index-pattern-id",
|
|
||||||
"title": "the-index-pattern-title",
|
|
||||||
},
|
|
||||||
"currentSavedSearch": Object {
|
|
||||||
"columns": Array [],
|
|
||||||
"destroy": [Function],
|
|
||||||
"id": "the-saved-search-id",
|
|
||||||
"searchSource": Object {},
|
|
||||||
"sort": Array [],
|
|
||||||
"title": "the-saved-search-title",
|
|
||||||
},
|
|
||||||
"indexPatterns": _temp {
|
|
||||||
"clearCache": [MockFunction],
|
|
||||||
"config": Object {},
|
|
||||||
"fieldFormats": Array [],
|
|
||||||
"get": [MockFunction],
|
|
||||||
"getDefault": [MockFunction],
|
|
||||||
"getFields": [MockFunction],
|
|
||||||
"getIds": [MockFunction],
|
|
||||||
"getTitles": [MockFunction],
|
|
||||||
"make": [MockFunction],
|
|
||||||
"refreshSavedObjectsCache": Object {},
|
|
||||||
"savedObjectsClient": Object {},
|
|
||||||
},
|
|
||||||
"kbnBaseUrl": "url",
|
|
||||||
"kibanaConfig": Object {
|
|
||||||
"get": [Function],
|
|
||||||
"has": [Function],
|
|
||||||
"set": [Function],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Component
|
|
||||||
query={
|
|
||||||
Object {
|
|
||||||
"query_string": Object {
|
|
||||||
"default_operator": "AND",
|
|
||||||
"query": "the-query",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ContextProvider>
|
|
||||||
</div>
|
|
||||||
`;
|
|
|
@ -1,62 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Data Frame: <StepCreateForm /> Minimal initialization 1`] = `
|
|
||||||
<div>
|
|
||||||
<ContextProvider
|
|
||||||
value={
|
|
||||||
Object {
|
|
||||||
"combinedQuery": Object {
|
|
||||||
"language": "the-query-language",
|
|
||||||
"query": "the-query-string",
|
|
||||||
},
|
|
||||||
"currentIndexPattern": Object {
|
|
||||||
"fields": Array [],
|
|
||||||
"id": "the-index-pattern-id",
|
|
||||||
"title": "the-index-pattern-title",
|
|
||||||
},
|
|
||||||
"currentSavedSearch": Object {
|
|
||||||
"columns": Array [],
|
|
||||||
"destroy": [Function],
|
|
||||||
"id": "the-saved-search-id",
|
|
||||||
"searchSource": Object {},
|
|
||||||
"sort": Array [],
|
|
||||||
"title": "the-saved-search-title",
|
|
||||||
},
|
|
||||||
"indexPatterns": _temp {
|
|
||||||
"clearCache": [MockFunction],
|
|
||||||
"config": Object {},
|
|
||||||
"fieldFormats": Array [],
|
|
||||||
"get": [MockFunction],
|
|
||||||
"getDefault": [MockFunction],
|
|
||||||
"getFields": [MockFunction],
|
|
||||||
"getIds": [MockFunction],
|
|
||||||
"getTitles": [MockFunction],
|
|
||||||
"make": [MockFunction],
|
|
||||||
"refreshSavedObjectsCache": Object {},
|
|
||||||
"savedObjectsClient": Object {},
|
|
||||||
},
|
|
||||||
"kbnBaseUrl": "url",
|
|
||||||
"kibanaConfig": Object {
|
|
||||||
"get": [Function],
|
|
||||||
"has": [Function],
|
|
||||||
"set": [Function],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Component
|
|
||||||
createIndexPattern={false}
|
|
||||||
onChange={[Function]}
|
|
||||||
overrides={
|
|
||||||
Object {
|
|
||||||
"created": false,
|
|
||||||
"indexPatternId": undefined,
|
|
||||||
"started": false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
transformConfig={Object {}}
|
|
||||||
transformId="the-transform-id"
|
|
||||||
/>
|
|
||||||
</ContextProvider>
|
|
||||||
</div>
|
|
||||||
`;
|
|
|
@ -1,79 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Data Frame: <PivotPreview /> Minimal initialization 1`] = `
|
|
||||||
<div>
|
|
||||||
<ContextProvider
|
|
||||||
value={
|
|
||||||
Object {
|
|
||||||
"combinedQuery": Object {
|
|
||||||
"language": "the-query-language",
|
|
||||||
"query": "the-query-string",
|
|
||||||
},
|
|
||||||
"currentIndexPattern": Object {
|
|
||||||
"fields": Array [],
|
|
||||||
"id": "the-index-pattern-id",
|
|
||||||
"title": "the-index-pattern-title",
|
|
||||||
},
|
|
||||||
"currentSavedSearch": Object {
|
|
||||||
"columns": Array [],
|
|
||||||
"destroy": [Function],
|
|
||||||
"id": "the-saved-search-id",
|
|
||||||
"searchSource": Object {},
|
|
||||||
"sort": Array [],
|
|
||||||
"title": "the-saved-search-title",
|
|
||||||
},
|
|
||||||
"indexPatterns": _temp {
|
|
||||||
"clearCache": [MockFunction],
|
|
||||||
"config": Object {},
|
|
||||||
"fieldFormats": Array [],
|
|
||||||
"get": [MockFunction],
|
|
||||||
"getDefault": [MockFunction],
|
|
||||||
"getFields": [MockFunction],
|
|
||||||
"getIds": [MockFunction],
|
|
||||||
"getTitles": [MockFunction],
|
|
||||||
"make": [MockFunction],
|
|
||||||
"refreshSavedObjectsCache": Object {},
|
|
||||||
"savedObjectsClient": Object {},
|
|
||||||
},
|
|
||||||
"kbnBaseUrl": "url",
|
|
||||||
"kibanaConfig": Object {
|
|
||||||
"get": [Function],
|
|
||||||
"has": [Function],
|
|
||||||
"set": [Function],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Component
|
|
||||||
aggs={
|
|
||||||
Object {
|
|
||||||
"the-agg-name": Object {
|
|
||||||
"agg": "avg",
|
|
||||||
"aggName": "the-group-by-agg-name",
|
|
||||||
"dropDownName": "the-group-by-drop-down-name",
|
|
||||||
"field": "the-agg-field",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
groupBy={
|
|
||||||
Object {
|
|
||||||
"the-group-by-name": Object {
|
|
||||||
"agg": "terms",
|
|
||||||
"aggName": "the-group-by-agg-name",
|
|
||||||
"dropDownName": "the-group-by-drop-down-name",
|
|
||||||
"field": "the-group-by-field",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
query={
|
|
||||||
Object {
|
|
||||||
"query_string": Object {
|
|
||||||
"default_operator": "AND",
|
|
||||||
"query": "the-query",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ContextProvider>
|
|
||||||
</div>
|
|
||||||
`;
|
|
|
@ -1,52 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Data Frame: <DefinePivotForm /> Minimal initialization 1`] = `
|
|
||||||
<div>
|
|
||||||
<ContextProvider
|
|
||||||
value={
|
|
||||||
Object {
|
|
||||||
"combinedQuery": Object {
|
|
||||||
"language": "the-query-language",
|
|
||||||
"query": "the-query-string",
|
|
||||||
},
|
|
||||||
"currentIndexPattern": Object {
|
|
||||||
"fields": Array [],
|
|
||||||
"id": "the-index-pattern-id",
|
|
||||||
"title": "the-index-pattern-title",
|
|
||||||
},
|
|
||||||
"currentSavedSearch": Object {
|
|
||||||
"columns": Array [],
|
|
||||||
"destroy": [Function],
|
|
||||||
"id": "the-saved-search-id",
|
|
||||||
"searchSource": Object {},
|
|
||||||
"sort": Array [],
|
|
||||||
"title": "the-saved-search-title",
|
|
||||||
},
|
|
||||||
"indexPatterns": _temp {
|
|
||||||
"clearCache": [MockFunction],
|
|
||||||
"config": Object {},
|
|
||||||
"fieldFormats": Array [],
|
|
||||||
"get": [MockFunction],
|
|
||||||
"getDefault": [MockFunction],
|
|
||||||
"getFields": [MockFunction],
|
|
||||||
"getIds": [MockFunction],
|
|
||||||
"getTitles": [MockFunction],
|
|
||||||
"make": [MockFunction],
|
|
||||||
"refreshSavedObjectsCache": Object {},
|
|
||||||
"savedObjectsClient": Object {},
|
|
||||||
},
|
|
||||||
"kbnBaseUrl": "url",
|
|
||||||
"kibanaConfig": Object {
|
|
||||||
"get": [Function],
|
|
||||||
"has": [Function],
|
|
||||||
"set": [Function],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Component
|
|
||||||
onChange={[Function]}
|
|
||||||
/>
|
|
||||||
</ContextProvider>
|
|
||||||
</div>
|
|
||||||
`;
|
|
|
@ -1,77 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Data Frame: <DefinePivotSummary /> Minimal initialization 1`] = `
|
|
||||||
<div>
|
|
||||||
<ContextProvider
|
|
||||||
value={
|
|
||||||
Object {
|
|
||||||
"combinedQuery": Object {
|
|
||||||
"language": "the-query-language",
|
|
||||||
"query": "the-query-string",
|
|
||||||
},
|
|
||||||
"currentIndexPattern": Object {
|
|
||||||
"fields": Array [],
|
|
||||||
"id": "the-index-pattern-id",
|
|
||||||
"title": "the-index-pattern-title",
|
|
||||||
},
|
|
||||||
"currentSavedSearch": Object {
|
|
||||||
"columns": Array [],
|
|
||||||
"destroy": [Function],
|
|
||||||
"id": "the-saved-search-id",
|
|
||||||
"searchSource": Object {},
|
|
||||||
"sort": Array [],
|
|
||||||
"title": "the-saved-search-title",
|
|
||||||
},
|
|
||||||
"indexPatterns": _temp {
|
|
||||||
"clearCache": [MockFunction],
|
|
||||||
"config": Object {},
|
|
||||||
"fieldFormats": Array [],
|
|
||||||
"get": [MockFunction],
|
|
||||||
"getDefault": [MockFunction],
|
|
||||||
"getFields": [MockFunction],
|
|
||||||
"getIds": [MockFunction],
|
|
||||||
"getTitles": [MockFunction],
|
|
||||||
"make": [MockFunction],
|
|
||||||
"refreshSavedObjectsCache": Object {},
|
|
||||||
"savedObjectsClient": Object {},
|
|
||||||
},
|
|
||||||
"kbnBaseUrl": "url",
|
|
||||||
"kibanaConfig": Object {
|
|
||||||
"get": [Function],
|
|
||||||
"has": [Function],
|
|
||||||
"set": [Function],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<StepDefineSummary
|
|
||||||
aggList={
|
|
||||||
Object {
|
|
||||||
"the-agg-name": Object {
|
|
||||||
"agg": "avg",
|
|
||||||
"aggName": "the-group-by-agg-name",
|
|
||||||
"dropDownName": "the-group-by-drop-down-name",
|
|
||||||
"field": "the-agg-field",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
groupByList={
|
|
||||||
Object {
|
|
||||||
"the-group-by-name": Object {
|
|
||||||
"agg": "terms",
|
|
||||||
"aggName": "the-group-by-agg-name",
|
|
||||||
"dropDownName": "the-group-by-drop-down-name",
|
|
||||||
"field": "the-group-by-field",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
isAdvancedPivotEditorEnabled={false}
|
|
||||||
isAdvancedSourceEditorEnabled={false}
|
|
||||||
searchQuery="the-search-query"
|
|
||||||
searchString="the-query"
|
|
||||||
sourceConfigUpdated={false}
|
|
||||||
valid={true}
|
|
||||||
/>
|
|
||||||
</ContextProvider>
|
|
||||||
</div>
|
|
||||||
`;
|
|
|
@ -1,134 +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, { Fragment, SFC } from 'react';
|
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
|
||||||
|
|
||||||
import {
|
|
||||||
EuiCodeBlock,
|
|
||||||
EuiFlexGroup,
|
|
||||||
EuiFlexItem,
|
|
||||||
EuiForm,
|
|
||||||
EuiFormRow,
|
|
||||||
EuiText,
|
|
||||||
} from '@elastic/eui';
|
|
||||||
|
|
||||||
import { useKibanaContext } from '../../../../../contexts/kibana';
|
|
||||||
|
|
||||||
import { AggListSummary } from '../aggregation_list';
|
|
||||||
import { GroupByListSummary } from '../group_by_list';
|
|
||||||
import { PivotPreview } from './pivot_preview';
|
|
||||||
|
|
||||||
import { getPivotQuery } from '../../../../common';
|
|
||||||
import { StepDefineExposedState } from './step_define_form';
|
|
||||||
|
|
||||||
const defaultSearch = '*';
|
|
||||||
const emptySearch = '';
|
|
||||||
|
|
||||||
export const StepDefineSummary: SFC<StepDefineExposedState> = ({
|
|
||||||
searchString,
|
|
||||||
searchQuery,
|
|
||||||
groupByList,
|
|
||||||
aggList,
|
|
||||||
}) => {
|
|
||||||
const kibanaContext = useKibanaContext();
|
|
||||||
|
|
||||||
const pivotQuery = getPivotQuery(searchQuery);
|
|
||||||
let useCodeBlock = false;
|
|
||||||
let displaySearch;
|
|
||||||
// searchString set to empty once source config editor used - display query instead
|
|
||||||
if (searchString === emptySearch) {
|
|
||||||
displaySearch = JSON.stringify(searchQuery, null, 2);
|
|
||||||
useCodeBlock = true;
|
|
||||||
} else if (searchString === defaultSearch) {
|
|
||||||
displaySearch = emptySearch;
|
|
||||||
} else {
|
|
||||||
displaySearch = searchString;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<EuiFlexGroup>
|
|
||||||
<EuiFlexItem grow={false} style={{ minWidth: '420px' }}>
|
|
||||||
<EuiForm>
|
|
||||||
{kibanaContext.currentSavedSearch.id === undefined && typeof searchString === 'string' && (
|
|
||||||
<Fragment>
|
|
||||||
<EuiFormRow
|
|
||||||
label={i18n.translate('xpack.ml.dataframe.stepDefineSummary.indexPatternLabel', {
|
|
||||||
defaultMessage: 'Index pattern',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<span>{kibanaContext.currentIndexPattern.title}</span>
|
|
||||||
</EuiFormRow>
|
|
||||||
{useCodeBlock === false && displaySearch !== emptySearch && (
|
|
||||||
<EuiFormRow
|
|
||||||
label={i18n.translate('xpack.ml.dataframe.stepDefineSummary.queryLabel', {
|
|
||||||
defaultMessage: 'Query',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<span>{displaySearch}</span>
|
|
||||||
</EuiFormRow>
|
|
||||||
)}
|
|
||||||
{useCodeBlock === true && displaySearch !== emptySearch && (
|
|
||||||
<EuiFormRow
|
|
||||||
label={i18n.translate(
|
|
||||||
'xpack.ml.dataframe.stepDefineSummary.queryCodeBlockLabel',
|
|
||||||
{
|
|
||||||
defaultMessage: 'Query',
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<EuiCodeBlock
|
|
||||||
language="js"
|
|
||||||
fontSize="s"
|
|
||||||
paddingSize="s"
|
|
||||||
color="light"
|
|
||||||
overflowHeight={300}
|
|
||||||
isCopyable
|
|
||||||
>
|
|
||||||
{displaySearch}
|
|
||||||
</EuiCodeBlock>
|
|
||||||
</EuiFormRow>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{kibanaContext.currentSavedSearch.id !== undefined && (
|
|
||||||
<EuiFormRow
|
|
||||||
label={i18n.translate('xpack.ml.dataframe.stepDefineSummary.savedSearchLabel', {
|
|
||||||
defaultMessage: 'Saved search',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<span>{kibanaContext.currentSavedSearch.title}</span>
|
|
||||||
</EuiFormRow>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<EuiFormRow
|
|
||||||
label={i18n.translate('xpack.ml.dataframe.stepDefineSummary.groupByLabel', {
|
|
||||||
defaultMessage: 'Group by',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<GroupByListSummary list={groupByList} />
|
|
||||||
</EuiFormRow>
|
|
||||||
|
|
||||||
<EuiFormRow
|
|
||||||
label={i18n.translate('xpack.ml.dataframe.stepDefineSummary.aggregationsLabel', {
|
|
||||||
defaultMessage: 'Aggregations',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<AggListSummary list={aggList} />
|
|
||||||
</EuiFormRow>
|
|
||||||
</EuiForm>
|
|
||||||
</EuiFlexItem>
|
|
||||||
|
|
||||||
<EuiFlexItem>
|
|
||||||
<EuiText>
|
|
||||||
<PivotPreview aggs={aggList} groupBy={groupByList} query={pivotQuery} />
|
|
||||||
</EuiText>
|
|
||||||
</EuiFlexItem>
|
|
||||||
</EuiFlexGroup>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -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 React from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import { uiModules } from 'ui/modules';
|
|
||||||
const module = uiModules.get('apps/ml', ['react']);
|
|
||||||
|
|
||||||
import { IndexPatterns } from 'ui/index_patterns';
|
|
||||||
import { I18nContext } from 'ui/i18n';
|
|
||||||
import { IPrivate } from 'ui/private';
|
|
||||||
import { timefilter } from 'ui/timefilter';
|
|
||||||
|
|
||||||
import { InjectorService } from '../../../../common/types/angular';
|
|
||||||
|
|
||||||
import { SearchItemsProvider } from '../../../jobs/new_job/utils/new_job_utils';
|
|
||||||
import { KibanaConfigTypeFix, KibanaContext } from '../../../contexts/kibana';
|
|
||||||
import { Page } from './page';
|
|
||||||
|
|
||||||
module.directive('mlNewDataFrame', ($injector: InjectorService) => {
|
|
||||||
return {
|
|
||||||
scope: {},
|
|
||||||
restrict: 'E',
|
|
||||||
link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
|
|
||||||
const indexPatterns = $injector.get<IndexPatterns>('indexPatterns');
|
|
||||||
const kbnBaseUrl = $injector.get<string>('kbnBaseUrl');
|
|
||||||
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
|
|
||||||
const Private = $injector.get<IPrivate>('Private');
|
|
||||||
|
|
||||||
timefilter.disableTimeRangeSelector();
|
|
||||||
timefilter.disableAutoRefreshSelector();
|
|
||||||
|
|
||||||
const createSearchItems = Private(SearchItemsProvider);
|
|
||||||
const { indexPattern, savedSearch, combinedQuery } = createSearchItems();
|
|
||||||
|
|
||||||
const kibanaContext = {
|
|
||||||
combinedQuery,
|
|
||||||
currentIndexPattern: indexPattern,
|
|
||||||
currentSavedSearch: savedSearch,
|
|
||||||
indexPatterns,
|
|
||||||
kbnBaseUrl,
|
|
||||||
kibanaConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<I18nContext>
|
|
||||||
<KibanaContext.Provider value={kibanaContext}>
|
|
||||||
<Page />
|
|
||||||
</KibanaContext.Provider>
|
|
||||||
</I18nContext>,
|
|
||||||
element[0]
|
|
||||||
);
|
|
||||||
|
|
||||||
element.on('$destroy', () => {
|
|
||||||
ReactDOM.unmountComponentAtNode(element[0]);
|
|
||||||
scope.$destroy();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,62 +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, { FC, Fragment } from 'react';
|
|
||||||
|
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
|
||||||
import { i18n } from '@kbn/i18n';
|
|
||||||
|
|
||||||
import {
|
|
||||||
EuiBetaBadge,
|
|
||||||
EuiPage,
|
|
||||||
EuiPageBody,
|
|
||||||
EuiPageContentBody,
|
|
||||||
EuiPageContentHeader,
|
|
||||||
EuiPageContentHeaderSection,
|
|
||||||
EuiSpacer,
|
|
||||||
EuiTitle,
|
|
||||||
} from '@elastic/eui';
|
|
||||||
|
|
||||||
import { NavigationMenu } from '../../../components/navigation_menu/navigation_menu';
|
|
||||||
import { Wizard } from './components/wizard';
|
|
||||||
|
|
||||||
export const Page: FC = () => (
|
|
||||||
<Fragment>
|
|
||||||
<NavigationMenu tabId="new_data_frame" />
|
|
||||||
<EuiPage>
|
|
||||||
<EuiPageBody>
|
|
||||||
<EuiPageContentHeader>
|
|
||||||
<EuiPageContentHeaderSection>
|
|
||||||
<EuiTitle>
|
|
||||||
<h1>
|
|
||||||
<FormattedMessage
|
|
||||||
id="xpack.ml.dataframe.transformsWizard.newDataFrameTitle"
|
|
||||||
defaultMessage="New data frame"
|
|
||||||
/>
|
|
||||||
<span> </span>
|
|
||||||
<EuiBetaBadge
|
|
||||||
label={i18n.translate('xpack.ml.dataframe.transformsWizard.betaBadgeLabel', {
|
|
||||||
defaultMessage: `Beta`,
|
|
||||||
})}
|
|
||||||
tooltipContent={i18n.translate(
|
|
||||||
'xpack.ml.dataframe.transformsWizard.betaBadgeTooltipContent',
|
|
||||||
{
|
|
||||||
defaultMessage: `Data frames are a beta feature. We'd love to hear your feedback.`,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</h1>
|
|
||||||
</EuiTitle>
|
|
||||||
</EuiPageContentHeaderSection>
|
|
||||||
</EuiPageContentHeader>
|
|
||||||
<EuiPageContentBody>
|
|
||||||
<EuiSpacer size="l" />
|
|
||||||
<Wizard />
|
|
||||||
</EuiPageContentBody>
|
|
||||||
</EuiPageBody>
|
|
||||||
</EuiPage>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
|
@ -1,49 +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';
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import { checkBasicLicense } from '../../../license/check_license';
|
|
||||||
import { checkCreateDataFrameTransformPrivilege } from '../../../privilege/check_privilege';
|
|
||||||
import {
|
|
||||||
loadCurrentIndexPattern,
|
|
||||||
loadCurrentSavedSearch,
|
|
||||||
loadIndexPatterns,
|
|
||||||
} from '../../../util/index_utils';
|
|
||||||
|
|
||||||
import {
|
|
||||||
getDataFrameCreateBreadcrumbs,
|
|
||||||
getDataFrameIndexOrSearchBreadcrumbs,
|
|
||||||
} from '../../breadcrumbs';
|
|
||||||
|
|
||||||
const wizardTemplate = `<ml-new-data-frame />`;
|
|
||||||
|
|
||||||
uiRoutes.when('/data_frames/new_transform/step/pivot?', {
|
|
||||||
template: wizardTemplate,
|
|
||||||
k7Breadcrumbs: getDataFrameCreateBreadcrumbs,
|
|
||||||
resolve: {
|
|
||||||
CheckLicense: checkBasicLicense,
|
|
||||||
privileges: checkCreateDataFrameTransformPrivilege,
|
|
||||||
indexPattern: loadCurrentIndexPattern,
|
|
||||||
savedSearch: loadCurrentSavedSearch,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
uiRoutes.when('/data_frames/new_transform', {
|
|
||||||
redirectTo: '/data_frames/new_transform/step/index_or_search',
|
|
||||||
});
|
|
||||||
|
|
||||||
uiRoutes.when('/data_frames/new_transform/step/index_or_search', {
|
|
||||||
template: '<ml-index-or-search />',
|
|
||||||
k7Breadcrumbs: getDataFrameIndexOrSearchBreadcrumbs,
|
|
||||||
resolve: {
|
|
||||||
CheckLicense: checkBasicLicense,
|
|
||||||
privileges: checkCreateDataFrameTransformPrivilege,
|
|
||||||
indexPatterns: loadIndexPatterns,
|
|
||||||
nextStepPath: () => '#data_frames/new_transform/step/pivot',
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,74 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Data Frame: Job List <Page /> Minimal initialization 1`] = `
|
|
||||||
<Fragment>
|
|
||||||
<NavigationMenu
|
|
||||||
tabId="data_frames"
|
|
||||||
/>
|
|
||||||
<TransformStatsBar
|
|
||||||
transformsList={Array []}
|
|
||||||
/>
|
|
||||||
<EuiPage
|
|
||||||
data-test-subj="mlPageDataFrame"
|
|
||||||
restrictWidth={false}
|
|
||||||
>
|
|
||||||
<EuiPageBody
|
|
||||||
restrictWidth={false}
|
|
||||||
>
|
|
||||||
<EuiPageContentHeader
|
|
||||||
responsive={true}
|
|
||||||
>
|
|
||||||
<EuiPageContentHeaderSection>
|
|
||||||
<EuiTitle>
|
|
||||||
<h1>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Data frame transforms"
|
|
||||||
id="xpack.ml.dataframe.transformList.dataFrameTitle"
|
|
||||||
values={Object {}}
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
|
|
||||||
</span>
|
|
||||||
<EuiBetaBadge
|
|
||||||
label="Beta"
|
|
||||||
tooltipContent="Data frames are a beta feature. We'd love to hear your feedback."
|
|
||||||
/>
|
|
||||||
</h1>
|
|
||||||
</EuiTitle>
|
|
||||||
</EuiPageContentHeaderSection>
|
|
||||||
<EuiPageContentHeaderSection>
|
|
||||||
<EuiFlexGroup
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<EuiFlexItem
|
|
||||||
grow={false}
|
|
||||||
>
|
|
||||||
<RefreshTransformListButton
|
|
||||||
isLoading={false}
|
|
||||||
onClick={[Function]}
|
|
||||||
/>
|
|
||||||
</EuiFlexItem>
|
|
||||||
<EuiFlexItem
|
|
||||||
grow={false}
|
|
||||||
>
|
|
||||||
<CreateTransformButton />
|
|
||||||
</EuiFlexItem>
|
|
||||||
</EuiFlexGroup>
|
|
||||||
</EuiPageContentHeaderSection>
|
|
||||||
</EuiPageContentHeader>
|
|
||||||
<EuiPageContentBody>
|
|
||||||
<EuiSpacer
|
|
||||||
size="l"
|
|
||||||
/>
|
|
||||||
<EuiPanel>
|
|
||||||
<DataFrameTransformList
|
|
||||||
isInitialized={false}
|
|
||||||
transforms={Array []}
|
|
||||||
transformsLoading={false}
|
|
||||||
/>
|
|
||||||
</EuiPanel>
|
|
||||||
</EuiPageContentBody>
|
|
||||||
</EuiPageBody>
|
|
||||||
</EuiPage>
|
|
||||||
</Fragment>
|
|
||||||
`;
|
|
|
@ -1,24 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Data Frame: Transform List <CreateTransformButton /> Minimal initialization 1`] = `
|
|
||||||
<EuiToolTip
|
|
||||||
content="Your license has expired. Please contact your administrator."
|
|
||||||
delay="regular"
|
|
||||||
position="top"
|
|
||||||
>
|
|
||||||
<EuiButton
|
|
||||||
data-test-subj="mlDataFramesButtonCreate"
|
|
||||||
disabled={true}
|
|
||||||
fill={true}
|
|
||||||
iconType="plusInCircle"
|
|
||||||
onClick={[Function]}
|
|
||||||
size="s"
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Create transform"
|
|
||||||
id="xpack.ml.dataframe.transformList.createDataFrameButton"
|
|
||||||
values={Object {}}
|
|
||||||
/>
|
|
||||||
</EuiButton>
|
|
||||||
</EuiToolTip>
|
|
||||||
`;
|
|
|
@ -1,51 +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, { SFC } from 'react';
|
|
||||||
|
|
||||||
import { EuiButton, EuiToolTip } from '@elastic/eui';
|
|
||||||
|
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
checkPermission,
|
|
||||||
createPermissionFailureMessage,
|
|
||||||
} from '../../../../../privilege/check_privilege';
|
|
||||||
|
|
||||||
import { moveToDataFrameWizard } from '../../../../common';
|
|
||||||
|
|
||||||
export const CreateTransformButton: SFC = () => {
|
|
||||||
const disabled =
|
|
||||||
!checkPermission('canCreateDataFrame') ||
|
|
||||||
!checkPermission('canPreviewDataFrame') ||
|
|
||||||
!checkPermission('canStartStopDataFrame');
|
|
||||||
|
|
||||||
const button = (
|
|
||||||
<EuiButton
|
|
||||||
disabled={disabled}
|
|
||||||
fill
|
|
||||||
onClick={moveToDataFrameWizard}
|
|
||||||
iconType="plusInCircle"
|
|
||||||
size="s"
|
|
||||||
data-test-subj="mlDataFramesButtonCreate"
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id="xpack.ml.dataframe.transformList.createDataFrameButton"
|
|
||||||
defaultMessage="Create transform"
|
|
||||||
/>
|
|
||||||
</EuiButton>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (disabled) {
|
|
||||||
return (
|
|
||||||
<EuiToolTip position="top" content={createPermissionFailureMessage('canCreateDataFrame')}>
|
|
||||||
{button}
|
|
||||||
</EuiToolTip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return button;
|
|
||||||
};
|
|
|
@ -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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { shallow } from 'enzyme';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { DataFrameTransformListRow } from '../../../../common';
|
|
||||||
import { DeleteAction } from './action_delete';
|
|
||||||
|
|
||||||
import dataFrameTransformListRow from '../../../../common/__mocks__/data_frame_transform_list_row.json';
|
|
||||||
|
|
||||||
describe('Data Frame: Transform List Actions <DeleteAction />', () => {
|
|
||||||
test('Minimal initialization', () => {
|
|
||||||
const item: DataFrameTransformListRow = dataFrameTransformListRow;
|
|
||||||
const props = {
|
|
||||||
disabled: false,
|
|
||||||
items: [item],
|
|
||||||
deleteTransform(d: DataFrameTransformListRow) {},
|
|
||||||
};
|
|
||||||
|
|
||||||
const wrapper = shallow(<DeleteAction {...props} />);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { shallow } from 'enzyme';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { DataFrameTransformListRow } from '../../../../common';
|
|
||||||
import { StartAction } from './action_start';
|
|
||||||
|
|
||||||
import dataFrameTransformListRow from '../../../../common/__mocks__/data_frame_transform_list_row.json';
|
|
||||||
|
|
||||||
describe('Data Frame: Transform List Actions <StartAction />', () => {
|
|
||||||
test('Minimal initialization', () => {
|
|
||||||
const item: DataFrameTransformListRow = dataFrameTransformListRow;
|
|
||||||
const props = {
|
|
||||||
disabled: false,
|
|
||||||
items: [item],
|
|
||||||
startTransform(d: DataFrameTransformListRow) {},
|
|
||||||
};
|
|
||||||
|
|
||||||
const wrapper = shallow(<StartAction {...props} />);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { shallow } from 'enzyme';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { DataFrameTransformListRow } from '../../../../common';
|
|
||||||
import { StopAction } from './action_stop';
|
|
||||||
|
|
||||||
import dataFrameTransformListRow from '../../../../common/__mocks__/data_frame_transform_list_row.json';
|
|
||||||
|
|
||||||
describe('Data Frame: Transform List Actions <StopAction />', () => {
|
|
||||||
test('Minimal initialization', () => {
|
|
||||||
const item: DataFrameTransformListRow = dataFrameTransformListRow;
|
|
||||||
const props = {
|
|
||||||
disabled: false,
|
|
||||||
items: [item],
|
|
||||||
stopTransform(d: DataFrameTransformListRow) {},
|
|
||||||
};
|
|
||||||
|
|
||||||
const wrapper = shallow(<StopAction {...props} />);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,35 +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 { Page } from './page';
|
|
||||||
|
|
||||||
module.directive('mlDataFramePage', () => {
|
|
||||||
return {
|
|
||||||
scope: {},
|
|
||||||
restrict: 'E',
|
|
||||||
link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
|
|
||||||
ReactDOM.render(
|
|
||||||
<I18nContext>
|
|
||||||
<Page />
|
|
||||||
</I18nContext>,
|
|
||||||
element[0]
|
|
||||||
);
|
|
||||||
|
|
||||||
element.on('$destroy', () => {
|
|
||||||
ReactDOM.unmountComponentAtNode(element[0]);
|
|
||||||
scope.$destroy();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import uiRoutes from 'ui/routes';
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import { checkBasicLicense } from '../../../license/check_license';
|
|
||||||
// @ts-ignore
|
|
||||||
import { checkGetDataFrameTransformsPrivilege } from '../../../privilege/check_privilege';
|
|
||||||
// @ts-ignore
|
|
||||||
import { loadIndexPatterns } from '../../../util/index_utils';
|
|
||||||
// @ts-ignore
|
|
||||||
import { getDataFrameBreadcrumbs } from '../../breadcrumbs';
|
|
||||||
|
|
||||||
const template = `<ml-data-frame-page />`;
|
|
||||||
|
|
||||||
uiRoutes.when('/data_frames/?', {
|
|
||||||
template,
|
|
||||||
k7Breadcrumbs: getDataFrameBreadcrumbs,
|
|
||||||
resolve: {
|
|
||||||
CheckLicense: checkBasicLicense,
|
|
||||||
privileges: checkGetDataFrameTransformsPrivilege,
|
|
||||||
indexPatterns: loadIndexPatterns,
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -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 { i18n } from '@kbn/i18n';
|
|
||||||
import { toastNotifications } from 'ui/notify';
|
|
||||||
import { ml } from '../../../../../services/ml_api_service';
|
|
||||||
import {
|
|
||||||
DataFrameTransformListRow,
|
|
||||||
refreshTransformList$,
|
|
||||||
REFRESH_TRANSFORM_LIST_STATE,
|
|
||||||
} from '../../../../common';
|
|
||||||
import {
|
|
||||||
DataFrameTransformEndpointRequest,
|
|
||||||
DataFrameTransformEndpointResult,
|
|
||||||
} from '../../components/transform_list/common';
|
|
||||||
|
|
||||||
import { mlMessageBarService } from '../../../../../../public/components/messagebar/messagebar_service';
|
|
||||||
|
|
||||||
export const deleteTransforms = async (dataFrames: DataFrameTransformListRow[]) => {
|
|
||||||
const dataFramesInfo: DataFrameTransformEndpointRequest[] = dataFrames.map(df => ({
|
|
||||||
id: df.config.id,
|
|
||||||
state: df.stats.state,
|
|
||||||
}));
|
|
||||||
const results: DataFrameTransformEndpointResult = await ml.dataFrame.deleteDataFrameTransforms(
|
|
||||||
dataFramesInfo
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const transformId in results) {
|
|
||||||
// hasOwnProperty check to ensure only properties on object itself, and not its prototypes
|
|
||||||
if (results.hasOwnProperty(transformId)) {
|
|
||||||
if (results[transformId].success === true) {
|
|
||||||
toastNotifications.addSuccess(
|
|
||||||
i18n.translate('xpack.ml.dataframe.transformList.deleteTransformSuccessMessage', {
|
|
||||||
defaultMessage: 'Request to delete data frame transform {transformId} acknowledged.',
|
|
||||||
values: { transformId },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
toastNotifications.addDanger(
|
|
||||||
i18n.translate('xpack.ml.dataframe.transformList.deleteTransformErrorMessage', {
|
|
||||||
defaultMessage: 'An error occurred deleting the data frame transform {transformId}',
|
|
||||||
values: { transformId },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
mlMessageBarService.notify.error(results[transformId].error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH);
|
|
||||||
};
|
|
|
@ -1,56 +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 { toastNotifications } from 'ui/notify';
|
|
||||||
import { ml } from '../../../../../services/ml_api_service';
|
|
||||||
|
|
||||||
import {
|
|
||||||
DataFrameTransformListRow,
|
|
||||||
refreshTransformList$,
|
|
||||||
REFRESH_TRANSFORM_LIST_STATE,
|
|
||||||
} from '../../../../common';
|
|
||||||
|
|
||||||
import {
|
|
||||||
DataFrameTransformEndpointRequest,
|
|
||||||
DataFrameTransformEndpointResult,
|
|
||||||
} from '../../components/transform_list/common';
|
|
||||||
|
|
||||||
import { mlMessageBarService } from '../../../../../../public/components/messagebar/messagebar_service';
|
|
||||||
|
|
||||||
export const startTransforms = async (dataFrames: DataFrameTransformListRow[]) => {
|
|
||||||
const dataFramesInfo: DataFrameTransformEndpointRequest[] = dataFrames.map(df => ({
|
|
||||||
id: df.config.id,
|
|
||||||
state: df.stats.state,
|
|
||||||
}));
|
|
||||||
const results: DataFrameTransformEndpointResult = await ml.dataFrame.startDataFrameTransforms(
|
|
||||||
dataFramesInfo
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const transformId in results) {
|
|
||||||
// hasOwnProperty check to ensure only properties on object itself, and not its prototypes
|
|
||||||
if (results.hasOwnProperty(transformId)) {
|
|
||||||
if (results[transformId].success === true) {
|
|
||||||
toastNotifications.addSuccess(
|
|
||||||
i18n.translate('xpack.ml.dataframe.transformList.startTransformSuccessMessage', {
|
|
||||||
defaultMessage: 'Request to start data frame transform {transformId} acknowledged.',
|
|
||||||
values: { transformId },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
toastNotifications.addDanger(
|
|
||||||
i18n.translate('xpack.ml.dataframe.transformList.startTransformErrorMessage', {
|
|
||||||
defaultMessage: 'An error occurred starting the data frame transform {transformId}',
|
|
||||||
values: { transformId },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
mlMessageBarService.notify.error(results[transformId].error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH);
|
|
||||||
};
|
|
|
@ -1,56 +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 { toastNotifications } from 'ui/notify';
|
|
||||||
import { ml } from '../../../../../services/ml_api_service';
|
|
||||||
|
|
||||||
import {
|
|
||||||
DataFrameTransformListRow,
|
|
||||||
refreshTransformList$,
|
|
||||||
REFRESH_TRANSFORM_LIST_STATE,
|
|
||||||
} from '../../../../common';
|
|
||||||
|
|
||||||
import {
|
|
||||||
DataFrameTransformEndpointRequest,
|
|
||||||
DataFrameTransformEndpointResult,
|
|
||||||
} from '../../components/transform_list/common';
|
|
||||||
|
|
||||||
import { mlMessageBarService } from '../../../../../../public/components/messagebar/messagebar_service';
|
|
||||||
|
|
||||||
export const stopTransforms = async (dataFrames: DataFrameTransformListRow[]) => {
|
|
||||||
const dataFramesInfo: DataFrameTransformEndpointRequest[] = dataFrames.map(df => ({
|
|
||||||
id: df.config.id,
|
|
||||||
state: df.stats.state,
|
|
||||||
}));
|
|
||||||
const results: DataFrameTransformEndpointResult = await ml.dataFrame.stopDataFrameTransforms(
|
|
||||||
dataFramesInfo
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const transformId in results) {
|
|
||||||
// hasOwnProperty check to ensure only properties on object itself, and not its prototypes
|
|
||||||
if (results.hasOwnProperty(transformId)) {
|
|
||||||
if (results[transformId].success === true) {
|
|
||||||
toastNotifications.addSuccess(
|
|
||||||
i18n.translate('xpack.ml.dataframe.transformList.stopTransformSuccessMessage', {
|
|
||||||
defaultMessage: 'Request to stop data frame transform {transformId} acknowledged.',
|
|
||||||
values: { transformId },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
toastNotifications.addDanger(
|
|
||||||
i18n.translate('xpack.ml.dataframe.transformList.stopTransformErrorMessage', {
|
|
||||||
defaultMessage: 'An error occurred stopping the data frame transform {transformId}',
|
|
||||||
values: { transformId },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
mlMessageBarService.notify.error(results[transformId].error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH);
|
|
||||||
};
|
|
|
@ -103,6 +103,12 @@ export const ExpandedRow: FC<Props> = ({ item }) => {
|
||||||
},
|
},
|
||||||
*/
|
*/
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Using `expand=false` here so the tabs themselves don't spread
|
||||||
|
// across the full width. The 100% width is used so the bottom line
|
||||||
|
// as well as the tab content spans across the full width.
|
||||||
|
// EuiTabbedContent would do that usually anyway,
|
||||||
|
// it just doesn't seem to work within certain layouts.
|
||||||
return (
|
return (
|
||||||
<EuiTabbedContent
|
<EuiTabbedContent
|
||||||
size="s"
|
size="s"
|
||||||
|
@ -110,6 +116,7 @@ export const ExpandedRow: FC<Props> = ({ item }) => {
|
||||||
initialSelectedTab={tabs[0]}
|
initialSelectedTab={tabs[0]}
|
||||||
onTabClick={() => {}}
|
onTabClick={() => {}}
|
||||||
expand={false}
|
expand={false}
|
||||||
|
style={{ width: '100%' }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
@import 'app';
|
@import 'app';
|
||||||
|
|
||||||
// Sub applications
|
// Sub applications
|
||||||
@import 'data_frame/index';
|
|
||||||
@import 'data_frame_analytics/index';
|
@import 'data_frame_analytics/index';
|
||||||
@import 'datavisualizer/index';
|
@import 'datavisualizer/index';
|
||||||
@import 'explorer/index'; // SASSTODO: This file needs to be rewritten
|
@import 'explorer/index'; // SASSTODO: This file needs to be rewritten
|
||||||
|
|
|
@ -88,42 +88,6 @@ export function checkFindFileStructurePrivilege(kbnUrl: any): Promise<Privileges
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkGetDataFrameTransformsPrivilege(kbnUrl: any): Promise<Privileges> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
getPrivileges().then(({ capabilities }) => {
|
|
||||||
privileges = capabilities;
|
|
||||||
// the minimum privilege for using ML with a basic license is being able to use the data frames.
|
|
||||||
// all other functionality is controlled by the return privileges object
|
|
||||||
if (privileges.canGetDataFrame) {
|
|
||||||
return resolve(privileges);
|
|
||||||
} else {
|
|
||||||
kbnUrl.redirect('/data_frames/access-denied');
|
|
||||||
return reject();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function checkCreateDataFrameTransformPrivilege(kbnUrl: any): Promise<Privileges> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
getPrivileges().then(({ capabilities }) => {
|
|
||||||
privileges = capabilities;
|
|
||||||
if (
|
|
||||||
privileges.canCreateDataFrame &&
|
|
||||||
privileges.canPreviewDataFrame &&
|
|
||||||
privileges.canStartStopDataFrame
|
|
||||||
) {
|
|
||||||
return resolve(privileges);
|
|
||||||
} else {
|
|
||||||
// if the user has no permission to create a data frame transform,
|
|
||||||
// redirect them back to the Data Frame Transforms Management page
|
|
||||||
kbnUrl.redirect('/data_frames');
|
|
||||||
return reject();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// check the privilege type and the license to see whether a user has permission to access a feature.
|
// check the privilege type and the license to see whether a user has permission to access a feature.
|
||||||
// takes the name of the privilege variable as specified in get_privileges.js
|
// takes the name of the privilege variable as specified in get_privileges.js
|
||||||
export function checkPermission(privilegeType: keyof Privileges) {
|
export function checkPermission(privilegeType: keyof Privileges) {
|
||||||
|
@ -168,21 +132,6 @@ export function createPermissionFailureMessage(privilegeType: keyof Privileges)
|
||||||
message = i18n.translate('xpack.ml.privilege.noPermission.runForecastsTooltip', {
|
message = i18n.translate('xpack.ml.privilege.noPermission.runForecastsTooltip', {
|
||||||
defaultMessage: 'You do not have permission to run forecasts.',
|
defaultMessage: 'You do not have permission to run forecasts.',
|
||||||
});
|
});
|
||||||
} else if (privilegeType === 'canCreateDataFrame') {
|
|
||||||
message = i18n.translate('xpack.ml.privilege.noPermission.createDataFrameTransformTooltip', {
|
|
||||||
defaultMessage: 'You do not have permission to create data frame transforms.',
|
|
||||||
});
|
|
||||||
} else if (privilegeType === 'canStartStopDataFrame') {
|
|
||||||
message = i18n.translate(
|
|
||||||
'xpack.ml.privilege.noPermission.startOrStopDataFrameTransformTooltip',
|
|
||||||
{
|
|
||||||
defaultMessage: 'You do not have permission to start or stop data frame transforms.',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else if (privilegeType === 'canDeleteDataFrame') {
|
|
||||||
message = i18n.translate('xpack.ml.privilege.noPermission.deleteDataFrameTransformTooltip', {
|
|
||||||
defaultMessage: 'You do not have permission to delete data frame transforms.',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return i18n.translate('xpack.ml.privilege.pleaseContactAdministratorTooltip', {
|
return i18n.translate('xpack.ml.privilege.pleaseContactAdministratorTooltip', {
|
||||||
defaultMessage: '{message} Please contact your administrator.',
|
defaultMessage: '{message} Please contact your administrator.',
|
||||||
|
|
|
@ -1,83 +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 chrome from 'ui/chrome';
|
|
||||||
|
|
||||||
import { http } from '../../services/http_service';
|
|
||||||
|
|
||||||
const basePath = chrome.addBasePath('/api/ml');
|
|
||||||
|
|
||||||
export const dataFrame = {
|
|
||||||
getDataFrameTransforms(transformId) {
|
|
||||||
const transformIdString = transformId !== undefined ? `/${transformId}` : '';
|
|
||||||
return http({
|
|
||||||
url: `${basePath}/_data_frame/transforms${transformIdString}`,
|
|
||||||
method: 'GET'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getDataFrameTransformsStats(transformId) {
|
|
||||||
if (transformId !== undefined) {
|
|
||||||
return http({
|
|
||||||
url: `${basePath}/_data_frame/transforms/${transformId}/_stats`,
|
|
||||||
method: 'GET'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return http({
|
|
||||||
url: `${basePath}/_data_frame/transforms/_stats`,
|
|
||||||
method: 'GET'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
createDataFrameTransform(transformId, transformConfig) {
|
|
||||||
return http({
|
|
||||||
url: `${basePath}/_data_frame/transforms/${transformId}`,
|
|
||||||
method: 'PUT',
|
|
||||||
data: transformConfig
|
|
||||||
});
|
|
||||||
},
|
|
||||||
deleteDataFrameTransforms(transformsInfo) {
|
|
||||||
return http({
|
|
||||||
url: `${basePath}/_data_frame/transforms/delete_transforms`,
|
|
||||||
method: 'POST',
|
|
||||||
data: {
|
|
||||||
transformsInfo
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getDataFrameTransformsPreview(obj) {
|
|
||||||
return http({
|
|
||||||
url: `${basePath}/_data_frame/transforms/_preview`,
|
|
||||||
method: 'POST',
|
|
||||||
data: obj
|
|
||||||
});
|
|
||||||
},
|
|
||||||
startDataFrameTransforms(transformsInfo) {
|
|
||||||
return http({
|
|
||||||
url: `${basePath}/_data_frame/transforms/start_transforms`,
|
|
||||||
method: 'POST',
|
|
||||||
data: {
|
|
||||||
transformsInfo,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
stopDataFrameTransforms(transformsInfo) {
|
|
||||||
return http({
|
|
||||||
url: `${basePath}/_data_frame/transforms/stop_transforms`,
|
|
||||||
method: 'POST',
|
|
||||||
data: {
|
|
||||||
transformsInfo,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getTransformAuditMessages(transformId) {
|
|
||||||
return http({
|
|
||||||
url: `${basePath}/_data_frame/transforms/${transformId}/messages`,
|
|
||||||
method: 'GET',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -8,10 +8,6 @@ import { Annotation } from '../../../common/types/annotations';
|
||||||
import { AggFieldNamePair } from '../../../common/types/fields';
|
import { AggFieldNamePair } from '../../../common/types/fields';
|
||||||
import { ExistingJobsAndGroups } from '../job_service';
|
import { ExistingJobsAndGroups } from '../job_service';
|
||||||
import { PrivilegesResponse } from '../../../common/types/privileges';
|
import { PrivilegesResponse } from '../../../common/types/privileges';
|
||||||
import {
|
|
||||||
DataFrameTransformEndpointRequest,
|
|
||||||
DataFrameTransformEndpointResult,
|
|
||||||
} from '../../data_frame/pages/transform_management/components/transform_list/common';
|
|
||||||
import { MlSummaryJobs } from '../../../common/types/jobs';
|
import { MlSummaryJobs } from '../../../common/types/jobs';
|
||||||
|
|
||||||
// TODO This is not a complete representation of all methods of `ml.*`.
|
// TODO This is not a complete representation of all methods of `ml.*`.
|
||||||
|
@ -48,23 +44,6 @@ declare interface Ml {
|
||||||
getAnalyticsAuditMessages(analyticsId: string): Promise<any>;
|
getAnalyticsAuditMessages(analyticsId: string): Promise<any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
dataFrame: {
|
|
||||||
getDataFrameTransforms(jobId?: string): Promise<any>;
|
|
||||||
getDataFrameTransformsStats(jobId?: string): Promise<any>;
|
|
||||||
createDataFrameTransform(jobId: string, jobConfig: any): Promise<any>;
|
|
||||||
deleteDataFrameTransforms(
|
|
||||||
jobsData: DataFrameTransformEndpointRequest[]
|
|
||||||
): Promise<DataFrameTransformEndpointResult>;
|
|
||||||
getDataFrameTransformsPreview(payload: any): Promise<any>;
|
|
||||||
startDataFrameTransforms(
|
|
||||||
jobsData: DataFrameTransformEndpointRequest[]
|
|
||||||
): Promise<DataFrameTransformEndpointResult>;
|
|
||||||
stopDataFrameTransforms(
|
|
||||||
jobsData: DataFrameTransformEndpointRequest[]
|
|
||||||
): Promise<DataFrameTransformEndpointResult>;
|
|
||||||
getTransformAuditMessages(transformId: string): Promise<any>;
|
|
||||||
};
|
|
||||||
|
|
||||||
hasPrivileges(obj: object): Promise<any>;
|
hasPrivileges(obj: object): Promise<any>;
|
||||||
|
|
||||||
checkMlPrivileges(): Promise<PrivilegesResponse>;
|
checkMlPrivileges(): Promise<PrivilegesResponse>;
|
||||||
|
|
|
@ -12,7 +12,6 @@ import chrome from 'ui/chrome';
|
||||||
import { http } from '../../services/http_service';
|
import { http } from '../../services/http_service';
|
||||||
|
|
||||||
import { annotations } from './annotations';
|
import { annotations } from './annotations';
|
||||||
import { dataFrame } from './data_frame';
|
|
||||||
import { dataFrameAnalytics } from './data_frame_analytics';
|
import { dataFrameAnalytics } from './data_frame_analytics';
|
||||||
import { filters } from './filters';
|
import { filters } from './filters';
|
||||||
import { results } from './results';
|
import { results } from './results';
|
||||||
|
@ -450,7 +449,6 @@ export const ml = {
|
||||||
},
|
},
|
||||||
|
|
||||||
annotations,
|
annotations,
|
||||||
dataFrame,
|
|
||||||
dataFrameAnalytics,
|
dataFrameAnalytics,
|
||||||
filters,
|
filters,
|
||||||
results,
|
results,
|
||||||
|
|
|
@ -205,119 +205,6 @@ export const elasticsearchJsPlugin = (Client, config, components) => {
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Currently the endpoint uses a default size of 100 unless a size is supplied.
|
|
||||||
// So until paging is supported in the UI, explicitly supply a size of 1000
|
|
||||||
// to match the max number of docs that the endpoint can return.
|
|
||||||
ml.getDataFrameTransforms = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_data_frame/transforms/<%=transformId%>',
|
|
||||||
req: {
|
|
||||||
transformId: {
|
|
||||||
type: 'string'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fmt: '/_data_frame/transforms/_all?size=1000',
|
|
||||||
}
|
|
||||||
],
|
|
||||||
method: 'GET'
|
|
||||||
});
|
|
||||||
|
|
||||||
ml.getDataFrameTransformsStats = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_data_frame/transforms/<%=transformId%>/_stats',
|
|
||||||
req: {
|
|
||||||
transformId: {
|
|
||||||
type: 'string'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Currently the endpoint uses a default size of 100 unless a size is supplied.
|
|
||||||
// So until paging is supported in the UI, explicitly supply a size of 1000
|
|
||||||
// to match the max number of docs that the endpoint can return.
|
|
||||||
fmt: '/_data_frame/transforms/_all/_stats?size=1000',
|
|
||||||
}
|
|
||||||
],
|
|
||||||
method: 'GET'
|
|
||||||
});
|
|
||||||
|
|
||||||
ml.createDataFrameTransform = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_data_frame/transforms/<%=transformId%>',
|
|
||||||
req: {
|
|
||||||
transformId: {
|
|
||||||
type: 'string'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
needBody: true,
|
|
||||||
method: 'PUT'
|
|
||||||
});
|
|
||||||
|
|
||||||
ml.deleteDataFrameTransform = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_data_frame/transforms/<%=transformId%>',
|
|
||||||
req: {
|
|
||||||
transformId: {
|
|
||||||
type: 'string'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
method: 'DELETE'
|
|
||||||
});
|
|
||||||
|
|
||||||
ml.getDataFrameTransformsPreview = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_data_frame/transforms/_preview'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
needBody: true,
|
|
||||||
method: 'POST'
|
|
||||||
});
|
|
||||||
|
|
||||||
ml.startDataFrameTransform = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_data_frame/transforms/<%=transformId%>/_start',
|
|
||||||
req: {
|
|
||||||
transformId: {
|
|
||||||
type: 'string'
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
method: 'POST'
|
|
||||||
});
|
|
||||||
|
|
||||||
ml.stopDataFrameTransform = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_data_frame/transforms/<%=transformId%>/_stop?&force=<%=force%>&wait_for_completion=<%waitForCompletion%>',
|
|
||||||
req: {
|
|
||||||
transformId: {
|
|
||||||
type: 'string'
|
|
||||||
},
|
|
||||||
force: {
|
|
||||||
type: 'boolean'
|
|
||||||
},
|
|
||||||
waitForCompletion: {
|
|
||||||
type: 'boolean'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
method: 'POST'
|
|
||||||
});
|
|
||||||
|
|
||||||
ml.deleteJob = ca({
|
ml.deleteJob = ca({
|
||||||
urls: [
|
urls: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -105,27 +105,19 @@ const fullClusterPrivileges = {
|
||||||
'cluster:monitor/xpack/ml/calendars/get': true,
|
'cluster:monitor/xpack/ml/calendars/get': true,
|
||||||
'cluster:admin/xpack/ml/filters/get': true,
|
'cluster:admin/xpack/ml/filters/get': true,
|
||||||
'cluster:monitor/xpack/ml/datafeeds/get': true,
|
'cluster:monitor/xpack/ml/datafeeds/get': true,
|
||||||
'cluster:admin/data_frame/start_task': true,
|
|
||||||
'cluster:admin/xpack/ml/filters/update': true,
|
'cluster:admin/xpack/ml/filters/update': true,
|
||||||
'cluster:admin/xpack/ml/calendars/events/post': true,
|
'cluster:admin/xpack/ml/calendars/events/post': true,
|
||||||
'cluster:monitor/data_frame/get': true,
|
|
||||||
'cluster:admin/xpack/ml/job/close': true,
|
'cluster:admin/xpack/ml/job/close': true,
|
||||||
'cluster:monitor/xpack/ml/datafeeds/stats/get': true,
|
'cluster:monitor/xpack/ml/datafeeds/stats/get': true,
|
||||||
'cluster:admin/data_frame/preview': true,
|
|
||||||
'cluster:admin/xpack/ml/calendars/jobs/update': true,
|
'cluster:admin/xpack/ml/calendars/jobs/update': true,
|
||||||
'cluster:admin/data_frame/put': true,
|
|
||||||
'cluster:admin/xpack/ml/calendars/put': true,
|
'cluster:admin/xpack/ml/calendars/put': true,
|
||||||
'cluster:monitor/data_frame/stats/get': true,
|
|
||||||
'cluster:admin/data_frame/stop': true,
|
|
||||||
'cluster:admin/xpack/ml/calendars/events/delete': true,
|
'cluster:admin/xpack/ml/calendars/events/delete': true,
|
||||||
'cluster:admin/xpack/ml/datafeeds/put': true,
|
'cluster:admin/xpack/ml/datafeeds/put': true,
|
||||||
'cluster:admin/xpack/ml/job/open': true,
|
'cluster:admin/xpack/ml/job/open': true,
|
||||||
'cluster:admin/xpack/ml/job/delete': true,
|
'cluster:admin/xpack/ml/job/delete': true,
|
||||||
'cluster:monitor/xpack/ml/job/get': true,
|
'cluster:monitor/xpack/ml/job/get': true,
|
||||||
'cluster:admin/data_frame/delete': true,
|
|
||||||
'cluster:admin/xpack/ml/job/put': true,
|
'cluster:admin/xpack/ml/job/put': true,
|
||||||
'cluster:admin/xpack/ml/job/update': true,
|
'cluster:admin/xpack/ml/job/update': true,
|
||||||
'cluster:admin/data_frame/start': true,
|
|
||||||
'cluster:admin/xpack/ml/calendars/delete': true,
|
'cluster:admin/xpack/ml/calendars/delete': true,
|
||||||
'cluster:monitor/xpack/ml/findfilestructure': true,
|
'cluster:monitor/xpack/ml/findfilestructure': true,
|
||||||
};
|
};
|
||||||
|
@ -144,27 +136,19 @@ const partialClusterPrivileges = {
|
||||||
'cluster:monitor/xpack/ml/calendars/get': true,
|
'cluster:monitor/xpack/ml/calendars/get': true,
|
||||||
'cluster:admin/xpack/ml/filters/get': false,
|
'cluster:admin/xpack/ml/filters/get': false,
|
||||||
'cluster:monitor/xpack/ml/datafeeds/get': true,
|
'cluster:monitor/xpack/ml/datafeeds/get': true,
|
||||||
'cluster:admin/data_frame/start_task': false,
|
|
||||||
'cluster:admin/xpack/ml/filters/update': false,
|
'cluster:admin/xpack/ml/filters/update': false,
|
||||||
'cluster:admin/xpack/ml/calendars/events/post': false,
|
'cluster:admin/xpack/ml/calendars/events/post': false,
|
||||||
'cluster:monitor/data_frame/get': false,
|
|
||||||
'cluster:admin/xpack/ml/job/close': false,
|
'cluster:admin/xpack/ml/job/close': false,
|
||||||
'cluster:monitor/xpack/ml/datafeeds/stats/get': true,
|
'cluster:monitor/xpack/ml/datafeeds/stats/get': true,
|
||||||
'cluster:admin/data_frame/preview': false,
|
|
||||||
'cluster:admin/xpack/ml/calendars/jobs/update': false,
|
'cluster:admin/xpack/ml/calendars/jobs/update': false,
|
||||||
'cluster:admin/data_frame/put': false,
|
|
||||||
'cluster:admin/xpack/ml/calendars/put': false,
|
'cluster:admin/xpack/ml/calendars/put': false,
|
||||||
'cluster:monitor/data_frame/stats/get': false,
|
|
||||||
'cluster:admin/data_frame/stop': false,
|
|
||||||
'cluster:admin/xpack/ml/calendars/events/delete': false,
|
'cluster:admin/xpack/ml/calendars/events/delete': false,
|
||||||
'cluster:admin/xpack/ml/datafeeds/put': false,
|
'cluster:admin/xpack/ml/datafeeds/put': false,
|
||||||
'cluster:admin/xpack/ml/job/open': false,
|
'cluster:admin/xpack/ml/job/open': false,
|
||||||
'cluster:admin/xpack/ml/job/delete': false,
|
'cluster:admin/xpack/ml/job/delete': false,
|
||||||
'cluster:monitor/xpack/ml/job/get': true,
|
'cluster:monitor/xpack/ml/job/get': true,
|
||||||
'cluster:admin/data_frame/delete': false,
|
|
||||||
'cluster:admin/xpack/ml/job/put': false,
|
'cluster:admin/xpack/ml/job/put': false,
|
||||||
'cluster:admin/xpack/ml/job/update': false,
|
'cluster:admin/xpack/ml/job/update': false,
|
||||||
'cluster:admin/data_frame/start': false,
|
|
||||||
'cluster:admin/xpack/ml/calendars/delete': false,
|
'cluster:admin/xpack/ml/calendars/delete': false,
|
||||||
'cluster:monitor/xpack/ml/findfilestructure': true,
|
'cluster:monitor/xpack/ml/findfilestructure': true,
|
||||||
};
|
};
|
||||||
|
|
|
@ -91,7 +91,7 @@ describe('check_privileges', () => {
|
||||||
describe('getPrivileges() - right number of capabilities', () => {
|
describe('getPrivileges() - right number of capabilities', () => {
|
||||||
test('es capabilities count', async done => {
|
test('es capabilities count', async done => {
|
||||||
const count = mlPrivileges.cluster.length;
|
const count = mlPrivileges.cluster.length;
|
||||||
expect(count).toBe(35);
|
expect(count).toBe(27);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ describe('check_privileges', () => {
|
||||||
);
|
);
|
||||||
const { capabilities } = await getPrivileges();
|
const { capabilities } = await getPrivileges();
|
||||||
const count = Object.keys(capabilities).length;
|
const count = Object.keys(capabilities).length;
|
||||||
expect(count).toBe(27);
|
expect(count).toBe(22);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -138,11 +138,6 @@ describe('check_privileges', () => {
|
||||||
expect(capabilities.canCreateFilter).toBe(false);
|
expect(capabilities.canCreateFilter).toBe(false);
|
||||||
expect(capabilities.canDeleteFilter).toBe(false);
|
expect(capabilities.canDeleteFilter).toBe(false);
|
||||||
expect(capabilities.canFindFileStructure).toBe(true);
|
expect(capabilities.canFindFileStructure).toBe(true);
|
||||||
expect(capabilities.canGetDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canDeleteDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canPreviewDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canCreateDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canStartStopDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canGetDataFrameAnalytics).toBe(true);
|
expect(capabilities.canGetDataFrameAnalytics).toBe(true);
|
||||||
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||||
|
@ -178,11 +173,6 @@ describe('check_privileges', () => {
|
||||||
expect(capabilities.canCreateFilter).toBe(true);
|
expect(capabilities.canCreateFilter).toBe(true);
|
||||||
expect(capabilities.canDeleteFilter).toBe(true);
|
expect(capabilities.canDeleteFilter).toBe(true);
|
||||||
expect(capabilities.canFindFileStructure).toBe(true);
|
expect(capabilities.canFindFileStructure).toBe(true);
|
||||||
expect(capabilities.canGetDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canDeleteDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canPreviewDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canCreateDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canStartStopDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canGetDataFrameAnalytics).toBe(true);
|
expect(capabilities.canGetDataFrameAnalytics).toBe(true);
|
||||||
expect(capabilities.canDeleteDataFrameAnalytics).toBe(true);
|
expect(capabilities.canDeleteDataFrameAnalytics).toBe(true);
|
||||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(true);
|
expect(capabilities.canCreateDataFrameAnalytics).toBe(true);
|
||||||
|
@ -218,11 +208,6 @@ describe('check_privileges', () => {
|
||||||
expect(capabilities.canCreateFilter).toBe(false);
|
expect(capabilities.canCreateFilter).toBe(false);
|
||||||
expect(capabilities.canDeleteFilter).toBe(false);
|
expect(capabilities.canDeleteFilter).toBe(false);
|
||||||
expect(capabilities.canFindFileStructure).toBe(true);
|
expect(capabilities.canFindFileStructure).toBe(true);
|
||||||
expect(capabilities.canGetDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canDeleteDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canPreviewDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canCreateDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canStartStopDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canGetDataFrameAnalytics).toBe(true);
|
expect(capabilities.canGetDataFrameAnalytics).toBe(true);
|
||||||
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||||
|
@ -258,11 +243,6 @@ describe('check_privileges', () => {
|
||||||
expect(capabilities.canCreateFilter).toBe(false);
|
expect(capabilities.canCreateFilter).toBe(false);
|
||||||
expect(capabilities.canDeleteFilter).toBe(false);
|
expect(capabilities.canDeleteFilter).toBe(false);
|
||||||
expect(capabilities.canFindFileStructure).toBe(true);
|
expect(capabilities.canFindFileStructure).toBe(true);
|
||||||
expect(capabilities.canGetDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canDeleteDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canPreviewDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canCreateDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canStartStopDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canGetDataFrameAnalytics).toBe(true);
|
expect(capabilities.canGetDataFrameAnalytics).toBe(true);
|
||||||
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||||
|
@ -298,11 +278,6 @@ describe('check_privileges', () => {
|
||||||
expect(capabilities.canCreateFilter).toBe(false);
|
expect(capabilities.canCreateFilter).toBe(false);
|
||||||
expect(capabilities.canDeleteFilter).toBe(false);
|
expect(capabilities.canDeleteFilter).toBe(false);
|
||||||
expect(capabilities.canFindFileStructure).toBe(true);
|
expect(capabilities.canFindFileStructure).toBe(true);
|
||||||
expect(capabilities.canGetDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canDeleteDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canPreviewDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canCreateDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canStartStopDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canGetDataFrameAnalytics).toBe(false);
|
expect(capabilities.canGetDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||||
|
@ -338,11 +313,6 @@ describe('check_privileges', () => {
|
||||||
expect(capabilities.canCreateFilter).toBe(false);
|
expect(capabilities.canCreateFilter).toBe(false);
|
||||||
expect(capabilities.canDeleteFilter).toBe(false);
|
expect(capabilities.canDeleteFilter).toBe(false);
|
||||||
expect(capabilities.canFindFileStructure).toBe(true);
|
expect(capabilities.canFindFileStructure).toBe(true);
|
||||||
expect(capabilities.canGetDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canDeleteDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canPreviewDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canCreateDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canStartStopDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canGetDataFrameAnalytics).toBe(false);
|
expect(capabilities.canGetDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||||
|
@ -378,11 +348,6 @@ describe('check_privileges', () => {
|
||||||
expect(capabilities.canCreateFilter).toBe(false);
|
expect(capabilities.canCreateFilter).toBe(false);
|
||||||
expect(capabilities.canDeleteFilter).toBe(false);
|
expect(capabilities.canDeleteFilter).toBe(false);
|
||||||
expect(capabilities.canFindFileStructure).toBe(false);
|
expect(capabilities.canFindFileStructure).toBe(false);
|
||||||
expect(capabilities.canGetDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canDeleteDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canPreviewDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canCreateDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canStartStopDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canGetDataFrameAnalytics).toBe(false);
|
expect(capabilities.canGetDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||||
|
@ -420,11 +385,6 @@ describe('check_privileges', () => {
|
||||||
expect(capabilities.canCreateFilter).toBe(true);
|
expect(capabilities.canCreateFilter).toBe(true);
|
||||||
expect(capabilities.canDeleteFilter).toBe(true);
|
expect(capabilities.canDeleteFilter).toBe(true);
|
||||||
expect(capabilities.canFindFileStructure).toBe(true);
|
expect(capabilities.canFindFileStructure).toBe(true);
|
||||||
expect(capabilities.canGetDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canDeleteDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canPreviewDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canCreateDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canStartStopDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canGetDataFrameAnalytics).toBe(true);
|
expect(capabilities.canGetDataFrameAnalytics).toBe(true);
|
||||||
expect(capabilities.canDeleteDataFrameAnalytics).toBe(true);
|
expect(capabilities.canDeleteDataFrameAnalytics).toBe(true);
|
||||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(true);
|
expect(capabilities.canCreateDataFrameAnalytics).toBe(true);
|
||||||
|
@ -460,11 +420,6 @@ describe('check_privileges', () => {
|
||||||
expect(capabilities.canCreateFilter).toBe(false);
|
expect(capabilities.canCreateFilter).toBe(false);
|
||||||
expect(capabilities.canDeleteFilter).toBe(false);
|
expect(capabilities.canDeleteFilter).toBe(false);
|
||||||
expect(capabilities.canFindFileStructure).toBe(true);
|
expect(capabilities.canFindFileStructure).toBe(true);
|
||||||
expect(capabilities.canGetDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canDeleteDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canPreviewDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canCreateDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canStartStopDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canGetDataFrameAnalytics).toBe(true);
|
expect(capabilities.canGetDataFrameAnalytics).toBe(true);
|
||||||
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||||
|
@ -500,11 +455,6 @@ describe('check_privileges', () => {
|
||||||
expect(capabilities.canCreateFilter).toBe(false);
|
expect(capabilities.canCreateFilter).toBe(false);
|
||||||
expect(capabilities.canDeleteFilter).toBe(false);
|
expect(capabilities.canDeleteFilter).toBe(false);
|
||||||
expect(capabilities.canFindFileStructure).toBe(true);
|
expect(capabilities.canFindFileStructure).toBe(true);
|
||||||
expect(capabilities.canGetDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canDeleteDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canPreviewDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canCreateDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canStartStopDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canGetDataFrameAnalytics).toBe(true);
|
expect(capabilities.canGetDataFrameAnalytics).toBe(true);
|
||||||
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||||
|
@ -540,11 +490,6 @@ describe('check_privileges', () => {
|
||||||
expect(capabilities.canCreateFilter).toBe(false);
|
expect(capabilities.canCreateFilter).toBe(false);
|
||||||
expect(capabilities.canDeleteFilter).toBe(false);
|
expect(capabilities.canDeleteFilter).toBe(false);
|
||||||
expect(capabilities.canFindFileStructure).toBe(true);
|
expect(capabilities.canFindFileStructure).toBe(true);
|
||||||
expect(capabilities.canGetDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canDeleteDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canPreviewDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canCreateDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canStartStopDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canGetDataFrameAnalytics).toBe(false);
|
expect(capabilities.canGetDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||||
|
@ -580,11 +525,6 @@ describe('check_privileges', () => {
|
||||||
expect(capabilities.canCreateFilter).toBe(false);
|
expect(capabilities.canCreateFilter).toBe(false);
|
||||||
expect(capabilities.canDeleteFilter).toBe(false);
|
expect(capabilities.canDeleteFilter).toBe(false);
|
||||||
expect(capabilities.canFindFileStructure).toBe(true);
|
expect(capabilities.canFindFileStructure).toBe(true);
|
||||||
expect(capabilities.canGetDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canDeleteDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canPreviewDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canCreateDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canStartStopDataFrame).toBe(true);
|
|
||||||
expect(capabilities.canGetDataFrameAnalytics).toBe(false);
|
expect(capabilities.canGetDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||||
|
@ -620,11 +560,6 @@ describe('check_privileges', () => {
|
||||||
expect(capabilities.canCreateFilter).toBe(false);
|
expect(capabilities.canCreateFilter).toBe(false);
|
||||||
expect(capabilities.canDeleteFilter).toBe(false);
|
expect(capabilities.canDeleteFilter).toBe(false);
|
||||||
expect(capabilities.canFindFileStructure).toBe(false);
|
expect(capabilities.canFindFileStructure).toBe(false);
|
||||||
expect(capabilities.canGetDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canDeleteDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canPreviewDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canCreateDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canStartStopDataFrame).toBe(false);
|
|
||||||
expect(capabilities.canGetDataFrameAnalytics).toBe(false);
|
expect(capabilities.canGetDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
expect(capabilities.canDeleteDataFrameAnalytics).toBe(false);
|
||||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||||
|
|
|
@ -129,14 +129,6 @@ function setFullGettingPrivileges(
|
||||||
privileges.canFindFileStructure = true;
|
privileges.canFindFileStructure = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data Frame Transforms
|
|
||||||
if (
|
|
||||||
forceTrue ||
|
|
||||||
(cluster['cluster:monitor/data_frame/get'] && cluster['cluster:monitor/data_frame/stats/get'])
|
|
||||||
) {
|
|
||||||
privileges.canGetDataFrame = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data Frame Analytics
|
// Data Frame Analytics
|
||||||
if (
|
if (
|
||||||
forceTrue ||
|
forceTrue ||
|
||||||
|
@ -234,28 +226,6 @@ function setFullActionPrivileges(
|
||||||
privileges.canDeleteFilter = true;
|
privileges.canDeleteFilter = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data Frame Transforms
|
|
||||||
if (forceTrue || cluster['cluster:admin/data_frame/put']) {
|
|
||||||
privileges.canCreateDataFrame = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forceTrue || cluster['cluster:admin/data_frame/delete']) {
|
|
||||||
privileges.canDeleteDataFrame = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forceTrue || cluster['cluster:admin/data_frame/preview']) {
|
|
||||||
privileges.canPreviewDataFrame = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
forceTrue ||
|
|
||||||
(cluster['cluster:admin/data_frame/start'] &&
|
|
||||||
cluster['cluster:admin/data_frame/start_task'] &&
|
|
||||||
cluster['cluster:admin/data_frame/stop'])
|
|
||||||
) {
|
|
||||||
privileges.canStartStopDataFrame = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data Frame Analytics
|
// Data Frame Analytics
|
||||||
if (
|
if (
|
||||||
forceTrue ||
|
forceTrue ||
|
||||||
|
@ -293,40 +263,10 @@ function setBasicGettingPrivileges(
|
||||||
if (forceTrue || cluster['cluster:monitor/xpack/ml/findfilestructure']) {
|
if (forceTrue || cluster['cluster:monitor/xpack/ml/findfilestructure']) {
|
||||||
privileges.canFindFileStructure = true;
|
privileges.canFindFileStructure = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data Frame Transforms
|
|
||||||
if (
|
|
||||||
forceTrue ||
|
|
||||||
(cluster['cluster:monitor/data_frame/get'] && cluster['cluster:monitor/data_frame/stats/get'])
|
|
||||||
) {
|
|
||||||
privileges.canGetDataFrame = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setBasicActionPrivileges(
|
function setBasicActionPrivileges(
|
||||||
cluster: ClusterPrivilege = {},
|
cluster: ClusterPrivilege = {},
|
||||||
privileges: Privileges,
|
privileges: Privileges,
|
||||||
forceTrue = false
|
forceTrue = false
|
||||||
) {
|
) {}
|
||||||
// Data Frame Transforms
|
|
||||||
if (forceTrue || cluster['cluster:admin/data_frame/put']) {
|
|
||||||
privileges.canCreateDataFrame = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forceTrue || cluster['cluster:admin/data_frame/delete']) {
|
|
||||||
privileges.canDeleteDataFrame = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forceTrue || cluster['cluster:admin/data_frame/preview']) {
|
|
||||||
privileges.canPreviewDataFrame = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
forceTrue ||
|
|
||||||
(cluster['cluster:admin/data_frame/start'] &&
|
|
||||||
cluster['cluster:admin/data_frame/start_task'] &&
|
|
||||||
cluster['cluster:admin/data_frame/stop'])
|
|
||||||
) {
|
|
||||||
privileges.canStartStopDataFrame = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,19 +6,11 @@
|
||||||
|
|
||||||
export const mlPrivileges = {
|
export const mlPrivileges = {
|
||||||
cluster: [
|
cluster: [
|
||||||
'cluster:monitor/data_frame/get',
|
|
||||||
'cluster:monitor/data_frame/stats/get',
|
|
||||||
'cluster:monitor/xpack/ml/job/get',
|
'cluster:monitor/xpack/ml/job/get',
|
||||||
'cluster:monitor/xpack/ml/job/stats/get',
|
'cluster:monitor/xpack/ml/job/stats/get',
|
||||||
'cluster:monitor/xpack/ml/datafeeds/get',
|
'cluster:monitor/xpack/ml/datafeeds/get',
|
||||||
'cluster:monitor/xpack/ml/datafeeds/stats/get',
|
'cluster:monitor/xpack/ml/datafeeds/stats/get',
|
||||||
'cluster:monitor/xpack/ml/calendars/get',
|
'cluster:monitor/xpack/ml/calendars/get',
|
||||||
'cluster:admin/data_frame/delete',
|
|
||||||
'cluster:admin/data_frame/preview',
|
|
||||||
'cluster:admin/data_frame/put',
|
|
||||||
'cluster:admin/data_frame/start',
|
|
||||||
'cluster:admin/data_frame/start_task',
|
|
||||||
'cluster:admin/data_frame/stop',
|
|
||||||
'cluster:admin/xpack/ml/job/put',
|
'cluster:admin/xpack/ml/job/put',
|
||||||
'cluster:admin/xpack/ml/job/delete',
|
'cluster:admin/xpack/ml/job/delete',
|
||||||
'cluster:admin/xpack/ml/job/update',
|
'cluster:admin/xpack/ml/job/update',
|
||||||
|
|
|
@ -1,145 +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 { callWithRequestType } from '../../../common/types/kibana';
|
|
||||||
import { DATA_FRAME_TRANSFORM_STATE } from '../../../public/data_frame/common';
|
|
||||||
import {
|
|
||||||
DataFrameTransformEndpointRequest,
|
|
||||||
DataFrameTransformEndpointResult,
|
|
||||||
} from '../../../public/data_frame/pages/transform_management/components/transform_list/common';
|
|
||||||
import { DataFrameTransformId } from '../../../public/data_frame/common/transform';
|
|
||||||
import { isRequestTimeout, fillResultsWithTimeouts } from './error_utils';
|
|
||||||
|
|
||||||
enum TRANSFORM_ACTIONS {
|
|
||||||
STOP = 'stop',
|
|
||||||
START = 'start',
|
|
||||||
DELETE = 'delete',
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StartTransformOptions {
|
|
||||||
transformId: DataFrameTransformId;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StopTransformOptions {
|
|
||||||
transformId: DataFrameTransformId;
|
|
||||||
force?: boolean;
|
|
||||||
waitForCompletion?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function transformServiceProvider(callWithRequest: callWithRequestType) {
|
|
||||||
async function deleteTransform(transformId: DataFrameTransformId) {
|
|
||||||
return callWithRequest('ml.deleteDataFrameTransform', { transformId });
|
|
||||||
}
|
|
||||||
|
|
||||||
async function stopTransform(options: StopTransformOptions) {
|
|
||||||
return callWithRequest('ml.stopDataFrameTransform', options);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function startTransform(options: StartTransformOptions) {
|
|
||||||
return callWithRequest('ml.startDataFrameTransform', options);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteTransforms(transformsInfo: DataFrameTransformEndpointRequest[]) {
|
|
||||||
const results: DataFrameTransformEndpointResult = {};
|
|
||||||
|
|
||||||
for (const transformInfo of transformsInfo) {
|
|
||||||
const transformId = transformInfo.id;
|
|
||||||
try {
|
|
||||||
if (transformInfo.state === DATA_FRAME_TRANSFORM_STATE.FAILED) {
|
|
||||||
try {
|
|
||||||
await stopTransform({
|
|
||||||
transformId,
|
|
||||||
force: true,
|
|
||||||
waitForCompletion: true,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
if (isRequestTimeout(e)) {
|
|
||||||
return fillResultsWithTimeouts({
|
|
||||||
results,
|
|
||||||
id: transformId,
|
|
||||||
items: transformsInfo,
|
|
||||||
action: TRANSFORM_ACTIONS.DELETE,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await deleteTransform(transformId);
|
|
||||||
results[transformId] = { success: true };
|
|
||||||
} catch (e) {
|
|
||||||
if (isRequestTimeout(e)) {
|
|
||||||
return fillResultsWithTimeouts({
|
|
||||||
results,
|
|
||||||
id: transformInfo.id,
|
|
||||||
items: transformsInfo,
|
|
||||||
action: TRANSFORM_ACTIONS.DELETE,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
results[transformId] = { success: false, error: JSON.stringify(e) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function startTransforms(transformsInfo: DataFrameTransformEndpointRequest[]) {
|
|
||||||
const results: DataFrameTransformEndpointResult = {};
|
|
||||||
|
|
||||||
for (const transformInfo of transformsInfo) {
|
|
||||||
const transformId = transformInfo.id;
|
|
||||||
try {
|
|
||||||
await startTransform({ transformId });
|
|
||||||
results[transformId] = { success: true };
|
|
||||||
} catch (e) {
|
|
||||||
if (isRequestTimeout(e)) {
|
|
||||||
return fillResultsWithTimeouts({
|
|
||||||
results,
|
|
||||||
id: transformId,
|
|
||||||
items: transformsInfo,
|
|
||||||
action: TRANSFORM_ACTIONS.START,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
results[transformId] = { success: false, error: JSON.stringify(e) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function stopTransforms(transformsInfo: DataFrameTransformEndpointRequest[]) {
|
|
||||||
const results: DataFrameTransformEndpointResult = {};
|
|
||||||
|
|
||||||
for (const transformInfo of transformsInfo) {
|
|
||||||
const transformId = transformInfo.id;
|
|
||||||
try {
|
|
||||||
await stopTransform({
|
|
||||||
transformId,
|
|
||||||
force:
|
|
||||||
transformInfo.state !== undefined
|
|
||||||
? transformInfo.state === DATA_FRAME_TRANSFORM_STATE.FAILED
|
|
||||||
: false,
|
|
||||||
waitForCompletion: true,
|
|
||||||
});
|
|
||||||
results[transformId] = { success: true };
|
|
||||||
} catch (e) {
|
|
||||||
if (isRequestTimeout(e)) {
|
|
||||||
return fillResultsWithTimeouts({
|
|
||||||
results,
|
|
||||||
id: transformId,
|
|
||||||
items: transformsInfo,
|
|
||||||
action: TRANSFORM_ACTIONS.STOP,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
results[transformId] = { success: false, error: JSON.stringify(e) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
deleteTransforms,
|
|
||||||
startTransforms,
|
|
||||||
stopTransforms,
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -37,8 +37,6 @@ import { notificationRoutes } from '../routes/notification_settings';
|
||||||
// @ts-ignore: could not find declaration file for module
|
// @ts-ignore: could not find declaration file for module
|
||||||
import { systemRoutes } from '../routes/system';
|
import { systemRoutes } from '../routes/system';
|
||||||
// @ts-ignore: could not find declaration file for module
|
// @ts-ignore: could not find declaration file for module
|
||||||
import { dataFrameRoutes } from '../routes/data_frame';
|
|
||||||
// @ts-ignore: could not find declaration file for module
|
|
||||||
import { dataFrameAnalyticsRoutes } from '../routes/data_frame_analytics';
|
import { dataFrameAnalyticsRoutes } from '../routes/data_frame_analytics';
|
||||||
// @ts-ignore: could not find declaration file for module
|
// @ts-ignore: could not find declaration file for module
|
||||||
import { dataRecognizer } from '../routes/modules';
|
import { dataRecognizer } from '../routes/modules';
|
||||||
|
@ -222,7 +220,6 @@ export class Plugin {
|
||||||
annotationRoutes(routeInitializationDeps);
|
annotationRoutes(routeInitializationDeps);
|
||||||
jobRoutes(routeInitializationDeps);
|
jobRoutes(routeInitializationDeps);
|
||||||
dataFeedRoutes(routeInitializationDeps);
|
dataFeedRoutes(routeInitializationDeps);
|
||||||
dataFrameRoutes(routeInitializationDeps);
|
|
||||||
dataFrameAnalyticsRoutes(routeInitializationDeps);
|
dataFrameAnalyticsRoutes(routeInitializationDeps);
|
||||||
indicesRoutes(routeInitializationDeps);
|
indicesRoutes(routeInitializationDeps);
|
||||||
jobValidationRoutes(extendedRouteInitializationDeps);
|
jobValidationRoutes(extendedRouteInitializationDeps);
|
||||||
|
|
|
@ -1,155 +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 { callWithRequestFactory } from '../client/call_with_request_factory';
|
|
||||||
import { wrapError } from '../client/errors';
|
|
||||||
import { transformAuditMessagesProvider } from '../models/data_frame/transform_audit_messages';
|
|
||||||
import { transformServiceProvider } from '../models/data_frame';
|
|
||||||
|
|
||||||
export function dataFrameRoutes({ commonRouteConfig, elasticsearchPlugin, route }) {
|
|
||||||
|
|
||||||
route({
|
|
||||||
method: 'GET',
|
|
||||||
path: '/api/ml/_data_frame/transforms',
|
|
||||||
handler(request) {
|
|
||||||
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
|
|
||||||
return callWithRequest('ml.getDataFrameTransforms')
|
|
||||||
.catch(resp => wrapError(resp));
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
...commonRouteConfig
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
route({
|
|
||||||
method: 'GET',
|
|
||||||
path: '/api/ml/_data_frame/transforms/{transformId}',
|
|
||||||
handler(request) {
|
|
||||||
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
|
|
||||||
const { transformId } = request.params;
|
|
||||||
return callWithRequest('ml.getDataFrameTransforms', { transformId })
|
|
||||||
.catch(resp => wrapError(resp));
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
...commonRouteConfig
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
route({
|
|
||||||
method: 'GET',
|
|
||||||
path: '/api/ml/_data_frame/transforms/_stats',
|
|
||||||
handler(request) {
|
|
||||||
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
|
|
||||||
return callWithRequest('ml.getDataFrameTransformsStats')
|
|
||||||
.catch(resp => wrapError(resp));
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
...commonRouteConfig
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
route({
|
|
||||||
method: 'GET',
|
|
||||||
path: '/api/ml/_data_frame/transforms/{transformId}/_stats',
|
|
||||||
handler(request) {
|
|
||||||
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
|
|
||||||
const { transformId } = request.params;
|
|
||||||
return callWithRequest('ml.getDataFrameTransformsStats', { transformId })
|
|
||||||
.catch(resp => wrapError(resp));
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
...commonRouteConfig
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
route({
|
|
||||||
method: 'PUT',
|
|
||||||
path: '/api/ml/_data_frame/transforms/{transformId}',
|
|
||||||
handler(request) {
|
|
||||||
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
|
|
||||||
const { transformId } = request.params;
|
|
||||||
return callWithRequest('ml.createDataFrameTransform', { body: request.payload, transformId })
|
|
||||||
.catch(resp => wrapError(resp));
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
...commonRouteConfig
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
route({
|
|
||||||
method: 'POST',
|
|
||||||
path: '/api/ml/_data_frame/transforms/delete_transforms',
|
|
||||||
handler(request) {
|
|
||||||
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
|
|
||||||
const { deleteTransforms } = transformServiceProvider(callWithRequest);
|
|
||||||
const { transformsInfo } = request.payload;
|
|
||||||
return deleteTransforms(transformsInfo)
|
|
||||||
.catch(resp => wrapError(resp));
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
...commonRouteConfig
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
route({
|
|
||||||
method: 'POST',
|
|
||||||
path: '/api/ml/_data_frame/transforms/_preview',
|
|
||||||
handler(request) {
|
|
||||||
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
|
|
||||||
return callWithRequest('ml.getDataFrameTransformsPreview', { body: request.payload })
|
|
||||||
.catch(resp => wrapError(resp));
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
...commonRouteConfig
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
route({
|
|
||||||
method: 'POST',
|
|
||||||
path: '/api/ml/_data_frame/transforms/start_transforms',
|
|
||||||
handler(request) {
|
|
||||||
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
|
|
||||||
const { startTransforms } = transformServiceProvider(callWithRequest);
|
|
||||||
const { transformsInfo } = request.payload;
|
|
||||||
return startTransforms(transformsInfo)
|
|
||||||
.catch(resp => wrapError(resp));
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
...commonRouteConfig
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
route({
|
|
||||||
method: 'POST',
|
|
||||||
path: '/api/ml/_data_frame/transforms/stop_transforms',
|
|
||||||
handler(request) {
|
|
||||||
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
|
|
||||||
const { stopTransforms } = transformServiceProvider(callWithRequest);
|
|
||||||
const { transformsInfo } = request.payload;
|
|
||||||
return stopTransforms(transformsInfo)
|
|
||||||
.catch(resp => wrapError(resp));
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
...commonRouteConfig
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
route({
|
|
||||||
method: 'GET',
|
|
||||||
path: '/api/ml/_data_frame/transforms/{transformId}/messages',
|
|
||||||
handler(request) {
|
|
||||||
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
|
|
||||||
const { getTransformAuditMessages } = transformAuditMessagesProvider(callWithRequest);
|
|
||||||
const { transformId } = request.params;
|
|
||||||
return getTransformAuditMessages(transformId)
|
|
||||||
.catch(resp => wrapError(resp));
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
...commonRouteConfig
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
63
x-pack/legacy/plugins/transform/common/constants.ts
Normal file
63
x-pack/legacy/plugins/transform/common/constants.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* 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 { LICENSE_TYPE_BASIC, LicenseType } from '../../../common/constants';
|
||||||
|
|
||||||
|
export const DEFAULT_REFRESH_INTERVAL_MS = 30000;
|
||||||
|
export const MINIMUM_REFRESH_INTERVAL_MS = 1000;
|
||||||
|
export const PROGRESS_REFRESH_INTERVAL_MS = 2000;
|
||||||
|
|
||||||
|
export const PLUGIN = {
|
||||||
|
ID: 'transform',
|
||||||
|
MINIMUM_LICENSE_REQUIRED: LICENSE_TYPE_BASIC as LicenseType,
|
||||||
|
getI18nName: (): string => {
|
||||||
|
return i18n.translate('xpack.transform.appName', {
|
||||||
|
defaultMessage: 'Transforms',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const API_BASE_PATH = '/api/transform/';
|
||||||
|
|
||||||
|
// Current df_admin permission requirements:
|
||||||
|
// 1. `read` on source index
|
||||||
|
// 2. `all` on source index to create and start transform
|
||||||
|
// 3. `all` on dest index (could be less tbd)
|
||||||
|
// 3. `monitor` cluster privilege
|
||||||
|
// 4. builtin `data_frame_transforms_admin`
|
||||||
|
// 5. builtin `kibana_user`
|
||||||
|
// 6. builtin `data_frame_transforms_user` (although this is probably included in the admin)
|
||||||
|
|
||||||
|
export const APP_CLUSTER_PRIVILEGES = [
|
||||||
|
'cluster:monitor/data_frame/get',
|
||||||
|
'cluster:monitor/data_frame/stats/get',
|
||||||
|
'cluster:admin/data_frame/delete',
|
||||||
|
'cluster:admin/data_frame/preview',
|
||||||
|
'cluster:admin/data_frame/put',
|
||||||
|
'cluster:admin/data_frame/start',
|
||||||
|
'cluster:admin/data_frame/start_task',
|
||||||
|
'cluster:admin/data_frame/stop',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Equivalent of capabilities.canGetTransform
|
||||||
|
export const APP_GET_TRANSFORM_CLUSTER_PRIVILEGES = [
|
||||||
|
'cluster.cluster:monitor/data_frame/get',
|
||||||
|
'cluster.cluster:monitor/data_frame/stats/get',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Equivalent of capabilities.canGetTransform
|
||||||
|
export const APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES = [
|
||||||
|
'cluster.cluster:monitor/data_frame/get',
|
||||||
|
'cluster.cluster:monitor/data_frame/stats/get',
|
||||||
|
'cluster.cluster:admin/data_frame/preview',
|
||||||
|
'cluster.cluster:admin/data_frame/put',
|
||||||
|
'cluster.cluster:admin/data_frame/start',
|
||||||
|
'cluster.cluster:admin/data_frame/start_task',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const APP_INDEX_PRIVILEGES = ['monitor'];
|
21
x-pack/legacy/plugins/transform/common/types/common.ts
Normal file
21
x-pack/legacy/plugins/transform/common/types/common.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* 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 interface Dictionary<TValue> {
|
||||||
|
[id: string]: TValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// converts a dictionary to an array. note this loses the dictionary `key` information.
|
||||||
|
// however it's able to retain the type information of the dictionary elements.
|
||||||
|
export function dictionaryToArray<TValue>(dict: Dictionary<TValue>): TValue[] {
|
||||||
|
return Object.keys(dict).map(key => dict[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A recursive partial type to allow passing nested partial attributes.
|
||||||
|
// Used for example for the optional `jobConfig.dest.results_field` property.
|
||||||
|
export type DeepPartial<T> = {
|
||||||
|
[P in keyof T]?: DeepPartial<T[P]>;
|
||||||
|
};
|
26
x-pack/legacy/plugins/transform/common/types/messages.ts
Normal file
26
x-pack/legacy/plugins/transform/common/types/messages.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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 interface AuditMessageBase {
|
||||||
|
message: string;
|
||||||
|
level: string;
|
||||||
|
timestamp: number;
|
||||||
|
node_name: string;
|
||||||
|
text?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuditMessage {
|
||||||
|
_index: string;
|
||||||
|
_type: string;
|
||||||
|
_id: string;
|
||||||
|
_score: null | number;
|
||||||
|
_source: TransformMessage;
|
||||||
|
sort?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransformMessage extends AuditMessageBase {
|
||||||
|
transform_id: string;
|
||||||
|
}
|
22
x-pack/legacy/plugins/transform/common/utils/date_utils.ts
Normal file
22
x-pack/legacy/plugins/transform/common/utils/date_utils.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// utility functions for handling dates
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import { formatDate } from '@elastic/eui/lib/services/format';
|
||||||
|
|
||||||
|
export function formatHumanReadableDate(ts: number) {
|
||||||
|
return formatDate(ts, 'MMMM Do YYYY');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatHumanReadableDateTime(ts: number) {
|
||||||
|
return formatDate(ts, 'MMMM Do YYYY, HH:mm');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatHumanReadableDateTimeSeconds(ts: number) {
|
||||||
|
return formatDate(ts, 'MMMM Do YYYY, HH:mm:ss');
|
||||||
|
}
|
41
x-pack/legacy/plugins/transform/common/utils/es_utils.ts
Normal file
41
x-pack/legacy/plugins/transform/common/utils/es_utils.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface WindowWithTextEncoder extends Window {
|
||||||
|
TextEncoder: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const windowWithTextEncoder = window as WindowWithTextEncoder;
|
||||||
|
|
||||||
|
function isValidIndexNameLength(indexName: string) {
|
||||||
|
if (
|
||||||
|
windowWithTextEncoder.TextEncoder &&
|
||||||
|
new windowWithTextEncoder.TextEncoder('utf-8').encode(indexName).length > 255
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If TextEncoder is not available just check for string.length
|
||||||
|
return indexName.length <= 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rules taken from
|
||||||
|
// https://github.com/elastic/elasticsearch/blob/master/docs/reference/indices/create-index.asciidoc
|
||||||
|
export function isValidIndexName(indexName: string) {
|
||||||
|
return (
|
||||||
|
// Lowercase only
|
||||||
|
indexName === indexName.toLowerCase() &&
|
||||||
|
// Cannot include \, /, *, ?, ", <, >, |, space character, comma, #, :
|
||||||
|
/^[^\*\\/\?"<>|\s,#:]+$/.test(indexName) &&
|
||||||
|
// Cannot start with -, _, +
|
||||||
|
/^[^-_\+]+$/.test(indexName.charAt(0)) &&
|
||||||
|
// Cannot be . or ..
|
||||||
|
(indexName !== '.' && indexName !== '..') &&
|
||||||
|
// Cannot be longer than 255 bytes (note it is bytes,
|
||||||
|
// so multi-byte characters will count towards the 255 limit faster)
|
||||||
|
isValidIndexNameLength(indexName)
|
||||||
|
);
|
||||||
|
}
|
17
x-pack/legacy/plugins/transform/common/utils/object_utils.ts
Normal file
17
x-pack/legacy/plugins/transform/common/utils/object_utils.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* 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 { idx } from '@kbn/elastic-idx';
|
||||||
|
|
||||||
|
// This is similar to lodash's get() except that it's TypeScript aware and is able to infer return types.
|
||||||
|
// It splits the attribute key string and uses reduce with an idx check to access nested attributes.
|
||||||
|
export const getNestedProperty = (
|
||||||
|
obj: Record<string, any>,
|
||||||
|
accessor: string,
|
||||||
|
defaultValue?: any
|
||||||
|
) => {
|
||||||
|
return accessor.split('.').reduce((o, i) => idx(o, _ => _[i]), obj) || defaultValue;
|
||||||
|
};
|
39
x-pack/legacy/plugins/transform/index.ts
Normal file
39
x-pack/legacy/plugins/transform/index.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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 { Legacy } from 'kibana';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
import { PLUGIN } from './common/constants';
|
||||||
|
import { Plugin as TransformPlugin } from './server/plugin';
|
||||||
|
import { createServerShim } from './server/shim';
|
||||||
|
|
||||||
|
export function transform(kibana: any) {
|
||||||
|
return new kibana.Plugin({
|
||||||
|
id: PLUGIN.ID,
|
||||||
|
configPrefix: 'xpack.transform',
|
||||||
|
publicDir: resolve(__dirname, 'public'),
|
||||||
|
require: ['kibana', 'elasticsearch', 'xpack_main'],
|
||||||
|
uiExports: {
|
||||||
|
styleSheetPaths: resolve(__dirname, 'public/app/index.scss'),
|
||||||
|
managementSections: ['plugins/transform'],
|
||||||
|
},
|
||||||
|
init(server: Legacy.Server) {
|
||||||
|
const { core, plugins } = createServerShim(server, PLUGIN.ID);
|
||||||
|
const transformPlugin = new TransformPlugin();
|
||||||
|
|
||||||
|
// Start plugin
|
||||||
|
transformPlugin.start(core, plugins);
|
||||||
|
|
||||||
|
// Register license checker
|
||||||
|
plugins.license.registerLicenseChecker(
|
||||||
|
server,
|
||||||
|
PLUGIN.ID,
|
||||||
|
PLUGIN.getI18nName(),
|
||||||
|
PLUGIN.MINIMUM_LICENSE_REQUIRED
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
66
x-pack/legacy/plugins/transform/public/app/app.tsx
Normal file
66
x-pack/legacy/plugins/transform/public/app/app.tsx
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* 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, { useContext, FC } from 'react';
|
||||||
|
import { render } from 'react-dom';
|
||||||
|
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
|
||||||
|
import { SectionError } from './components';
|
||||||
|
import { CLIENT_BASE_PATH, SECTION_SLUG } from './constants';
|
||||||
|
import { getAppProviders } from './app_dependencies';
|
||||||
|
import { AuthorizationContext } from './lib/authorization';
|
||||||
|
import { AppDependencies } from '../shim';
|
||||||
|
|
||||||
|
import { CreateTransformSection } from './sections/create_transform';
|
||||||
|
import { TransformManagementSection } from './sections/transform_management';
|
||||||
|
|
||||||
|
export const App: FC = () => {
|
||||||
|
const { apiError } = useContext(AuthorizationContext);
|
||||||
|
|
||||||
|
if (apiError) {
|
||||||
|
return (
|
||||||
|
<SectionError
|
||||||
|
title={
|
||||||
|
<FormattedMessage
|
||||||
|
id="xpack.transform.app.checkingPrivilegesErrorMessage"
|
||||||
|
defaultMessage="Error fetching user privileges from the server."
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
error={apiError}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-test-subj="transformApp">
|
||||||
|
<Switch>
|
||||||
|
<Route
|
||||||
|
path={`${CLIENT_BASE_PATH}/${SECTION_SLUG.CREATE_TRANSFORM}/:savedObjectId`}
|
||||||
|
component={CreateTransformSection}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path={`${CLIENT_BASE_PATH}/${SECTION_SLUG.HOME}`}
|
||||||
|
component={TransformManagementSection}
|
||||||
|
/>
|
||||||
|
<Redirect from={`${CLIENT_BASE_PATH}`} to={`${CLIENT_BASE_PATH}/${SECTION_SLUG.HOME}`} />
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const renderReact = (elem: Element, appDependencies: AppDependencies) => {
|
||||||
|
const Providers = getAppProviders(appDependencies);
|
||||||
|
|
||||||
|
render(
|
||||||
|
<Providers>
|
||||||
|
<App />
|
||||||
|
</Providers>,
|
||||||
|
elem
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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, { createContext, useContext, ReactNode } from 'react';
|
||||||
|
import { HashRouter } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { API_BASE_PATH } from '../../common/constants';
|
||||||
|
import { AuthorizationProvider } from './lib/authorization';
|
||||||
|
import { AppDependencies } from '../shim';
|
||||||
|
|
||||||
|
let DependenciesContext: React.Context<AppDependencies>;
|
||||||
|
|
||||||
|
const setAppDependencies = (deps: AppDependencies) => {
|
||||||
|
DependenciesContext = createContext<AppDependencies>(deps);
|
||||||
|
return DependenciesContext.Provider;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAppDependencies = () => {
|
||||||
|
if (!DependenciesContext) {
|
||||||
|
throw new Error(`The app dependencies Context hasn't been set.
|
||||||
|
Use the "setAppDependencies()" method when bootstrapping the app.`);
|
||||||
|
}
|
||||||
|
return useContext<AppDependencies>(DependenciesContext);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAppProviders = (deps: AppDependencies) => {
|
||||||
|
const I18nContext = deps.core.i18n.Context;
|
||||||
|
|
||||||
|
// Create App dependencies context and get its provider
|
||||||
|
const AppDependenciesProvider = setAppDependencies(deps);
|
||||||
|
|
||||||
|
return ({ children }: { children: ReactNode }) => (
|
||||||
|
<AuthorizationProvider
|
||||||
|
privilegesEndpoint={deps.core.http.basePath.prepend(`${API_BASE_PATH}privileges`)}
|
||||||
|
>
|
||||||
|
<I18nContext>
|
||||||
|
<HashRouter>
|
||||||
|
<AppDependenciesProvider value={deps}>{children}</AppDependenciesProvider>
|
||||||
|
</HashRouter>
|
||||||
|
</I18nContext>
|
||||||
|
</AuthorizationProvider>
|
||||||
|
);
|
||||||
|
};
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import { isAggName } from './aggregations';
|
import { isAggName } from './aggregations';
|
||||||
|
|
||||||
describe('Data Frame: Aggregations', () => {
|
describe('Transform: Aggregations', () => {
|
||||||
test('isAggName()', () => {
|
test('isAggName()', () => {
|
||||||
expect(isAggName('avg(responsetime)')).toEqual(true);
|
expect(isAggName('avg(responsetime)')).toEqual(true);
|
||||||
expect(isAggName('avg_responsetime')).toEqual(true);
|
expect(isAggName('avg_responsetime')).toEqual(true);
|
|
@ -22,22 +22,22 @@ export {
|
||||||
useRefreshTransformList,
|
useRefreshTransformList,
|
||||||
CreateRequestBody,
|
CreateRequestBody,
|
||||||
PreviewRequestBody,
|
PreviewRequestBody,
|
||||||
DataFrameTransformId,
|
TransformId,
|
||||||
DataFrameTransformPivotConfig,
|
TransformPivotConfig,
|
||||||
IndexName,
|
IndexName,
|
||||||
IndexPattern,
|
IndexPattern,
|
||||||
REFRESH_TRANSFORM_LIST_STATE,
|
REFRESH_TRANSFORM_LIST_STATE,
|
||||||
} from './transform';
|
} from './transform';
|
||||||
export { DATA_FRAME_TRANSFORM_LIST_COLUMN, DataFrameTransformListRow } from './transform_list';
|
export { TRANSFORM_LIST_COLUMN, TransformListRow } from './transform_list';
|
||||||
export {
|
export {
|
||||||
getTransformProgress,
|
getTransformProgress,
|
||||||
isCompletedBatchTransform,
|
isCompletedBatchTransform,
|
||||||
isDataFrameTransformStats,
|
isTransformStats,
|
||||||
DataFrameTransformStats,
|
TransformStats,
|
||||||
DATA_FRAME_MODE,
|
TRANSFORM_MODE,
|
||||||
DATA_FRAME_TRANSFORM_STATE,
|
TRANSFORM_STATE,
|
||||||
} from './transform_stats';
|
} from './transform_stats';
|
||||||
export { moveToDataFrameWizard, getDiscoverUrl } from './navigation';
|
export { getDiscoverUrl } from './navigation';
|
||||||
export {
|
export {
|
||||||
getEsAggFromAggConfig,
|
getEsAggFromAggConfig,
|
||||||
isPivotAggsConfigWithUiSupport,
|
isPivotAggsConfigWithUiSupport,
|
|
@ -4,15 +4,15 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import { Redirect } from 'react-router-dom';
|
||||||
import rison from 'rison-node';
|
import rison from 'rison-node';
|
||||||
|
|
||||||
export function moveToDataFrameWizard() {
|
import { CLIENT_BASE_PATH, SECTION_SLUG } from '../constants';
|
||||||
window.location.href = '#/data_frames/new_transform';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a url for navigating to Discover page.
|
* Gets a url for navigating to Discover page.
|
||||||
* @param indexPatternId Index pattern id.
|
* @param indexPatternId Index pattern ID.
|
||||||
* @param baseUrl Base url.
|
* @param baseUrl Base url.
|
||||||
*/
|
*/
|
||||||
export function getDiscoverUrl(indexPatternId: string, baseUrl: string): string {
|
export function getDiscoverUrl(indexPatternId: string, baseUrl: string): string {
|
||||||
|
@ -27,3 +27,11 @@ export function getDiscoverUrl(indexPatternId: string, baseUrl: string): string
|
||||||
|
|
||||||
return `${baseUrl}${hash}`;
|
return `${baseUrl}${hash}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const RedirectToTransformManagement: FC = () => (
|
||||||
|
<Redirect from={`${CLIENT_BASE_PATH}`} to={`${CLIENT_BASE_PATH}/${SECTION_SLUG.HOME}`} />
|
||||||
|
);
|
||||||
|
|
||||||
|
export const RedirectToCreateTransform: FC<{ savedObjectId: string }> = ({ savedObjectId }) => (
|
||||||
|
<Redirect to={`${CLIENT_BASE_PATH}/${SECTION_SLUG.CREATE_TRANSFORM}/${savedObjectId}`} />
|
||||||
|
);
|
|
@ -6,8 +6,8 @@
|
||||||
|
|
||||||
import { PivotGroupByConfig } from '../common';
|
import { PivotGroupByConfig } from '../common';
|
||||||
|
|
||||||
import { StepDefineExposedState } from '../pages/data_frame_new_pivot/components/step_define/step_define_form';
|
import { StepDefineExposedState } from '../sections/create_transform/components/step_define/step_define_form';
|
||||||
import { StepDetailsExposedState } from '../pages/data_frame_new_pivot/components/step_details/step_details_form';
|
import { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form';
|
||||||
|
|
||||||
import { PIVOT_SUPPORTED_GROUP_BY_AGGS } from './pivot_group_by';
|
import { PIVOT_SUPPORTED_GROUP_BY_AGGS } from './pivot_group_by';
|
||||||
import { PivotAggsConfig, PIVOT_SUPPORTED_AGGS } from './pivot_aggs';
|
import { PivotAggsConfig, PIVOT_SUPPORTED_AGGS } from './pivot_aggs';
|
||||||
|
@ -24,7 +24,7 @@ const defaultQuery: PivotQuery = { query_string: { query: '*' } };
|
||||||
const matchAllQuery: PivotQuery = { match_all: {} };
|
const matchAllQuery: PivotQuery = { match_all: {} };
|
||||||
const simpleQuery: PivotQuery = { query_string: { query: 'airline:AAL' } };
|
const simpleQuery: PivotQuery = { query_string: { query: 'airline:AAL' } };
|
||||||
|
|
||||||
describe('Data Frame: Common', () => {
|
describe('Transform: Common', () => {
|
||||||
test('isSimpleQuery()', () => {
|
test('isSimpleQuery()', () => {
|
||||||
expect(isSimpleQuery(defaultQuery)).toBe(true);
|
expect(isSimpleQuery(defaultQuery)).toBe(true);
|
||||||
expect(isSimpleQuery(matchAllQuery)).toBe(false);
|
expect(isSimpleQuery(matchAllQuery)).toBe(false);
|
|
@ -9,10 +9,10 @@ import { DefaultOperator } from 'elasticsearch';
|
||||||
import { IndexPattern } from 'ui/index_patterns';
|
import { IndexPattern } from 'ui/index_patterns';
|
||||||
|
|
||||||
import { dictionaryToArray } from '../../../common/types/common';
|
import { dictionaryToArray } from '../../../common/types/common';
|
||||||
import { SavedSearchQuery } from '../../contexts/kibana';
|
import { SavedSearchQuery } from '../lib/kibana';
|
||||||
|
|
||||||
import { StepDefineExposedState } from '../pages/data_frame_new_pivot/components/step_define/step_define_form';
|
import { StepDefineExposedState } from '../sections/create_transform/components/step_define/step_define_form';
|
||||||
import { StepDetailsExposedState } from '../pages/data_frame_new_pivot/components/step_details/step_details_form';
|
import { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getEsAggFromAggConfig,
|
getEsAggFromAggConfig,
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* 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 { isTransformIdValid } from './transform';
|
||||||
|
|
||||||
|
describe('Transform: isTransformIdValid()', () => {
|
||||||
|
test('returns true for job ID: "good_job-name"', () => {
|
||||||
|
expect(isTransformIdValid('good_job-name')).toBe(true);
|
||||||
|
});
|
||||||
|
test('returns false for job ID: "_bad_job-name"', () => {
|
||||||
|
expect(isTransformIdValid('_bad_job-name')).toBe(false);
|
||||||
|
});
|
||||||
|
test('returns false for job ID: "bad_job-name_"', () => {
|
||||||
|
expect(isTransformIdValid('bad_job-name_')).toBe(false);
|
||||||
|
});
|
||||||
|
test('returns false for job ID: "-bad_job-name"', () => {
|
||||||
|
expect(isTransformIdValid('-bad_job-name')).toBe(false);
|
||||||
|
});
|
||||||
|
test('returns false for job ID: "bad_job-name-"', () => {
|
||||||
|
expect(isTransformIdValid('bad_job-name-')).toBe(false);
|
||||||
|
});
|
||||||
|
test('returns false for job ID: "bad&job-name"', () => {
|
||||||
|
expect(isTransformIdValid('bad&job-name')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
|
@ -9,17 +9,18 @@ import { BehaviorSubject } from 'rxjs';
|
||||||
import { filter, distinctUntilChanged } from 'rxjs/operators';
|
import { filter, distinctUntilChanged } from 'rxjs/operators';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import { isJobIdValid } from '../../../common/util/job_utils';
|
|
||||||
|
|
||||||
import { PivotAggDict } from './pivot_aggs';
|
import { PivotAggDict } from './pivot_aggs';
|
||||||
import { PivotGroupByDict } from './pivot_group_by';
|
import { PivotGroupByDict } from './pivot_group_by';
|
||||||
|
|
||||||
export const isTransformIdValid = isJobIdValid;
|
|
||||||
|
|
||||||
export type IndexName = string;
|
export type IndexName = string;
|
||||||
export type IndexPattern = string;
|
export type IndexPattern = string;
|
||||||
export type DataFrameTransformId = string;
|
export type TransformId = string;
|
||||||
|
|
||||||
|
// Transform name must contain lowercase alphanumeric (a-z and 0-9), hyphens or underscores;
|
||||||
|
// It must also start and end with an alphanumeric character.
|
||||||
|
export function isTransformIdValid(transformId: TransformId) {
|
||||||
|
return /^[a-z0-9\-\_]+$/g.test(transformId) && !/^([_-].*)?(.*[_-])?$/g.test(transformId);
|
||||||
|
}
|
||||||
|
|
||||||
export interface PreviewRequestBody {
|
export interface PreviewRequestBody {
|
||||||
pivot: {
|
pivot: {
|
||||||
|
@ -45,8 +46,8 @@ export interface CreateRequestBody extends PreviewRequestBody {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataFrameTransformPivotConfig extends CreateRequestBody {
|
export interface TransformPivotConfig extends CreateRequestBody {
|
||||||
id: DataFrameTransformId;
|
id: TransformId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum REFRESH_TRANSFORM_LIST_STATE {
|
export enum REFRESH_TRANSFORM_LIST_STATE {
|
|
@ -4,20 +4,20 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { DataFrameTransformId, DataFrameTransformPivotConfig } from './transform';
|
import { TransformId, TransformPivotConfig } from './transform';
|
||||||
import { DataFrameTransformStats } from './transform_stats';
|
import { TransformStats } from './transform_stats';
|
||||||
|
|
||||||
// Used to pass on attribute names to table columns
|
// Used to pass on attribute names to table columns
|
||||||
export enum DATA_FRAME_TRANSFORM_LIST_COLUMN {
|
export enum TRANSFORM_LIST_COLUMN {
|
||||||
CONFIG_DEST_INDEX = 'config.dest.index',
|
CONFIG_DEST_INDEX = 'config.dest.index',
|
||||||
CONFIG_SOURCE_INDEX = 'config.source.index',
|
CONFIG_SOURCE_INDEX = 'config.source.index',
|
||||||
DESCRIPTION = 'config.description',
|
DESCRIPTION = 'config.description',
|
||||||
ID = 'id',
|
ID = 'id',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataFrameTransformListRow {
|
export interface TransformListRow {
|
||||||
id: DataFrameTransformId;
|
id: TransformId;
|
||||||
config: DataFrameTransformPivotConfig;
|
config: TransformPivotConfig;
|
||||||
mode?: string; // added property on client side to allow filtering by this field
|
mode?: string; // added property on client side to allow filtering by this field
|
||||||
stats: DataFrameTransformStats;
|
stats: TransformStats;
|
||||||
}
|
}
|
|
@ -4,24 +4,24 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import mockDataFrameTransformListRow from './__mocks__/data_frame_transform_list_row.json';
|
import mockTransformListRow from './__mocks__/transform_list_row.json';
|
||||||
import mockDataFrameTransformStats from './__mocks__/data_frame_transform_stats.json';
|
import mockTransformStats from './__mocks__/transform_stats.json';
|
||||||
|
|
||||||
import { DataFrameTransformListRow } from './transform_list';
|
import { TransformListRow } from './transform_list';
|
||||||
import { getTransformProgress, isCompletedBatchTransform } from './transform_stats';
|
import { getTransformProgress, isCompletedBatchTransform } from './transform_stats';
|
||||||
|
|
||||||
const getRow = (statsId: string) => {
|
const getRow = (statsId: string) => {
|
||||||
return {
|
return {
|
||||||
...(mockDataFrameTransformListRow as DataFrameTransformListRow),
|
...(mockTransformListRow as TransformListRow),
|
||||||
stats: {
|
stats: {
|
||||||
...mockDataFrameTransformStats.transforms.find(
|
...mockTransformStats.transforms.find(
|
||||||
(stats: DataFrameTransformListRow['stats']) => stats.id === statsId
|
(stats: TransformListRow['stats']) => stats.id === statsId
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Data Frame: Transform stats.', () => {
|
describe('Transform: Transform stats.', () => {
|
||||||
test('getTransformProgress()', () => {
|
test('getTransformProgress()', () => {
|
||||||
// At the moment, any kind of stopped jobs don't include progress information.
|
// At the moment, any kind of stopped jobs don't include progress information.
|
||||||
// We cannot infer progress for now from an unfinished job that has been stopped for now.
|
// We cannot infer progress for now from an unfinished job that has been stopped for now.
|
|
@ -6,11 +6,11 @@
|
||||||
|
|
||||||
import { idx } from '@kbn/elastic-idx';
|
import { idx } from '@kbn/elastic-idx';
|
||||||
|
|
||||||
import { DataFrameTransformId } from './transform';
|
import { TransformId } from './transform';
|
||||||
import { DataFrameTransformListRow } from './transform_list';
|
import { TransformListRow } from './transform_list';
|
||||||
|
|
||||||
// reflects https://github.com/elastic/elasticsearch/blob/master/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/transforms/DataFrameTransformStats.java#L243
|
// reflects https://github.com/elastic/elasticsearch/blob/master/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/transforms/DataFrameTransformStats.java#L243
|
||||||
export enum DATA_FRAME_TRANSFORM_STATE {
|
export enum TRANSFORM_STATE {
|
||||||
ABORTING = 'aborting',
|
ABORTING = 'aborting',
|
||||||
FAILED = 'failed',
|
FAILED = 'failed',
|
||||||
INDEXING = 'indexing',
|
INDEXING = 'indexing',
|
||||||
|
@ -19,13 +19,13 @@ export enum DATA_FRAME_TRANSFORM_STATE {
|
||||||
STOPPING = 'stopping',
|
STOPPING = 'stopping',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum DATA_FRAME_MODE {
|
export enum TRANSFORM_MODE {
|
||||||
BATCH = 'batch',
|
BATCH = 'batch',
|
||||||
CONTINUOUS = 'continuous',
|
CONTINUOUS = 'continuous',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataFrameTransformStats {
|
export interface TransformStats {
|
||||||
id: DataFrameTransformId;
|
id: TransformId;
|
||||||
checkpointing: {
|
checkpointing: {
|
||||||
last: {
|
last: {
|
||||||
checkpoint: number;
|
checkpoint: number;
|
||||||
|
@ -61,19 +61,19 @@ export interface DataFrameTransformStats {
|
||||||
trigger_count: number;
|
trigger_count: number;
|
||||||
};
|
};
|
||||||
reason?: string;
|
reason?: string;
|
||||||
state: DATA_FRAME_TRANSFORM_STATE;
|
state: TRANSFORM_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isDataFrameTransformStats(arg: any): arg is DataFrameTransformStats {
|
export function isTransformStats(arg: any): arg is TransformStats {
|
||||||
return (
|
return (
|
||||||
typeof arg === 'object' &&
|
typeof arg === 'object' &&
|
||||||
arg !== null &&
|
arg !== null &&
|
||||||
{}.hasOwnProperty.call(arg, 'state') &&
|
{}.hasOwnProperty.call(arg, 'state') &&
|
||||||
Object.values(DATA_FRAME_TRANSFORM_STATE).includes(arg.state)
|
Object.values(TRANSFORM_STATE).includes(arg.state)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTransformProgress(item: DataFrameTransformListRow) {
|
export function getTransformProgress(item: TransformListRow) {
|
||||||
if (isCompletedBatchTransform(item)) {
|
if (isCompletedBatchTransform(item)) {
|
||||||
return 100;
|
return 100;
|
||||||
}
|
}
|
||||||
|
@ -83,12 +83,12 @@ export function getTransformProgress(item: DataFrameTransformListRow) {
|
||||||
return progress !== undefined ? Math.round(progress) : undefined;
|
return progress !== undefined ? Math.round(progress) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCompletedBatchTransform(item: DataFrameTransformListRow) {
|
export function isCompletedBatchTransform(item: TransformListRow) {
|
||||||
// If `checkpoint=1`, `sync` is missing from the config and state is stopped,
|
// If `checkpoint=1`, `sync` is missing from the config and state is stopped,
|
||||||
// then this is a completed batch data frame transform.
|
// then this is a completed batch transform.
|
||||||
return (
|
return (
|
||||||
item.stats.checkpointing.last.checkpoint === 1 &&
|
item.stats.checkpointing.last.checkpoint === 1 &&
|
||||||
item.config.sync === undefined &&
|
item.config.sync === undefined &&
|
||||||
item.stats.state === DATA_FRAME_TRANSFORM_STATE.STOPPED
|
item.stats.state === TRANSFORM_STATE.STOPPED
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -4,7 +4,5 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { getTransformsFactory } from './get_transforms';
|
export { SectionError } from './section_error';
|
||||||
export { deleteTransforms } from './delete_transform';
|
export { SectionLoading } from './section_loading';
|
||||||
export { startTransforms } from './start_transform';
|
|
||||||
export { stopTransforms } from './stop_transform';
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* 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 { EuiIcon, EuiToolTip } from '@elastic/eui';
|
||||||
|
import { AuditMessageBase } from '../../../common/types/messages';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
message: AuditMessageBase;
|
||||||
|
showTooltip?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [INFO, WARNING, ERROR] = ['info', 'warning', 'error'];
|
||||||
|
|
||||||
|
export const JobIcon: FC<Props> = ({ message, showTooltip = false }) => {
|
||||||
|
if (message === undefined) {
|
||||||
|
return <span />;
|
||||||
|
}
|
||||||
|
|
||||||
|
let color = 'primary';
|
||||||
|
const icon = 'alert';
|
||||||
|
|
||||||
|
if (message.level === INFO) {
|
||||||
|
color = 'primary';
|
||||||
|
} else if (message.level === WARNING) {
|
||||||
|
color = 'warning';
|
||||||
|
} else if (message.level === ERROR) {
|
||||||
|
color = 'danger';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showTooltip) {
|
||||||
|
return (
|
||||||
|
<EuiToolTip position="bottom" content={message.text}>
|
||||||
|
<EuiIcon type={icon} color={color} />
|
||||||
|
</EuiToolTip>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <EuiIcon type={icon} color={color} />;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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 { EuiCallOut, EuiSpacer } from '@elastic/eui';
|
||||||
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title: React.ReactNode;
|
||||||
|
error: {
|
||||||
|
data: {
|
||||||
|
error: string;
|
||||||
|
cause?: string[];
|
||||||
|
message?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
actions?: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SectionError: React.FunctionComponent<Props> = ({
|
||||||
|
title,
|
||||||
|
error,
|
||||||
|
actions,
|
||||||
|
...rest
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
error: errorString,
|
||||||
|
cause, // wrapEsError() on the server adds a "cause" array
|
||||||
|
message,
|
||||||
|
} = error.data;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EuiCallOut title={title} color="danger" iconType="alert" {...rest}>
|
||||||
|
{cause ? message || errorString : <p>{message || errorString}</p>}
|
||||||
|
{cause && (
|
||||||
|
<Fragment>
|
||||||
|
<EuiSpacer size="s" />
|
||||||
|
<ul>
|
||||||
|
{cause.map((causeMsg, i) => (
|
||||||
|
<li key={i}>{causeMsg}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
{actions ? actions : null}
|
||||||
|
</EuiCallOut>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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 {
|
||||||
|
EuiEmptyPrompt,
|
||||||
|
EuiLoadingSpinner,
|
||||||
|
EuiText,
|
||||||
|
EuiFlexGroup,
|
||||||
|
EuiFlexItem,
|
||||||
|
EuiTextColor,
|
||||||
|
} from '@elastic/eui';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
inline?: boolean;
|
||||||
|
children: React.ReactNode;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SectionLoading: React.FunctionComponent<Props> = ({ inline, children, ...rest }) => {
|
||||||
|
if (inline) {
|
||||||
|
return (
|
||||||
|
<EuiFlexGroup justifyContent="flexStart" alignItems="center" gutterSize="s">
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<EuiLoadingSpinner size="m" />
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<EuiText {...rest}>
|
||||||
|
<EuiTextColor color="subdued">{children}</EuiTextColor>
|
||||||
|
</EuiText>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EuiEmptyPrompt
|
||||||
|
title={<EuiLoadingSpinner size="xl" />}
|
||||||
|
body={<EuiText color="subdued">{children}</EuiText>}
|
||||||
|
data-test-subj="sectionLoading"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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 CLIENT_BASE_PATH = '/management/elasticsearch/transform';
|
||||||
|
|
||||||
|
export enum SECTION_SLUG {
|
||||||
|
HOME = 'transform_management',
|
||||||
|
CREATE_TRANSFORM = 'create_transform',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TRANSFORM_DOC_PATHS {
|
||||||
|
default = 'docs.html',
|
||||||
|
plugins = 'plugins.html',
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI Metric constants
|
||||||
|
export const UIM_APP_NAME = 'transform';
|
||||||
|
export const UIM_TRANSFORM_LIST_LOAD = 'transform_list_load';
|
||||||
|
export const UIM_TRANSFORM_CREATE = 'transform_create';
|
||||||
|
export const UIM_TRANSFORM_DELETE = 'transform_delete';
|
||||||
|
export const UIM_TRANSFORM_DELETE_MANY = 'transform_delete_many';
|
||||||
|
export const UIM_TRANSFORM_SHOW_DETAILS_CLICK = 'transform_show_details_click';
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* 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 { PreviewRequestBody, TransformId } from '../../common';
|
||||||
|
|
||||||
|
import { TransformEndpointRequest } from '../use_api_types';
|
||||||
|
|
||||||
|
const apiFactory = () => ({
|
||||||
|
getTransforms(transformId?: TransformId): Promise<any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve([]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getTransformsStats(transformId?: TransformId): Promise<any> {
|
||||||
|
if (transformId !== undefined) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve([]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve([]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
createTransform(transformId: TransformId, transformConfig: any): Promise<any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve([]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteTransforms(transformsInfo: TransformEndpointRequest[]) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve([]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getTransformsPreview(obj: PreviewRequestBody): Promise<any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve([]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
startTransforms(transformsInfo: TransformEndpointRequest[]) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve([]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
stopTransforms(transformsInfo: TransformEndpointRequest[]) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve([]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getTransformAuditMessages(transformId: TransformId): Promise<any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve([]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
esSearch(payload: any) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve([]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getIndices() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve([]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useApi = () => {
|
||||||
|
return apiFactory();
|
||||||
|
};
|
11
x-pack/legacy/plugins/transform/public/app/hooks/index.ts
Normal file
11
x-pack/legacy/plugins/transform/public/app/hooks/index.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* 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 { useApi } from './use_api';
|
||||||
|
export { useGetTransforms } from './use_get_transforms';
|
||||||
|
export { useDeleteTransforms } from './use_delete_transform';
|
||||||
|
export { useStartTransforms } from './use_start_transform';
|
||||||
|
export { useStopTransforms } from './use_stop_transform';
|
103
x-pack/legacy/plugins/transform/public/app/hooks/use_api.ts
Normal file
103
x-pack/legacy/plugins/transform/public/app/hooks/use_api.ts
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* 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 { useAppDependencies } from '../app_dependencies';
|
||||||
|
|
||||||
|
import { PreviewRequestBody, TransformId } from '../common';
|
||||||
|
|
||||||
|
import { http } from '../services/http_service';
|
||||||
|
|
||||||
|
import { EsIndex, TransformEndpointRequest, TransformEndpointResult } from './use_api_types';
|
||||||
|
|
||||||
|
const apiFactory = (basePath: string, indicesBasePath: string) => ({
|
||||||
|
getTransforms(transformId?: TransformId): Promise<any> {
|
||||||
|
const transformIdString = transformId !== undefined ? `/${transformId}` : '';
|
||||||
|
return http({
|
||||||
|
url: `${basePath}/transforms${transformIdString}`,
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getTransformsStats(transformId?: TransformId): Promise<any> {
|
||||||
|
if (transformId !== undefined) {
|
||||||
|
return http({
|
||||||
|
url: `${basePath}/transforms/${transformId}/_stats`,
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return http({
|
||||||
|
url: `${basePath}/transforms/_stats`,
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
createTransform(transformId: TransformId, transformConfig: any): Promise<any> {
|
||||||
|
return http({
|
||||||
|
url: `${basePath}/transforms/${transformId}`,
|
||||||
|
method: 'PUT',
|
||||||
|
data: transformConfig,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteTransforms(transformsInfo: TransformEndpointRequest[]) {
|
||||||
|
return http({
|
||||||
|
url: `${basePath}/delete_transforms`,
|
||||||
|
method: 'POST',
|
||||||
|
data: transformsInfo,
|
||||||
|
}) as Promise<TransformEndpointResult>;
|
||||||
|
},
|
||||||
|
getTransformsPreview(obj: PreviewRequestBody): Promise<any> {
|
||||||
|
return http({
|
||||||
|
url: `${basePath}/transforms/_preview`,
|
||||||
|
method: 'POST',
|
||||||
|
data: obj,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
startTransforms(transformsInfo: TransformEndpointRequest[]) {
|
||||||
|
return http({
|
||||||
|
url: `${basePath}/start_transforms`,
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
transformsInfo,
|
||||||
|
},
|
||||||
|
}) as Promise<TransformEndpointResult>;
|
||||||
|
},
|
||||||
|
stopTransforms(transformsInfo: TransformEndpointRequest[]) {
|
||||||
|
return http({
|
||||||
|
url: `${basePath}/stop_transforms`,
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
transformsInfo,
|
||||||
|
},
|
||||||
|
}) as Promise<TransformEndpointResult>;
|
||||||
|
},
|
||||||
|
getTransformAuditMessages(transformId: TransformId): Promise<any> {
|
||||||
|
return http({
|
||||||
|
url: `${basePath}/transforms/${transformId}/messages`,
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
esSearch(payload: any) {
|
||||||
|
return http({
|
||||||
|
url: `${basePath}/es_search`,
|
||||||
|
method: 'POST',
|
||||||
|
data: payload,
|
||||||
|
}) as Promise<any>;
|
||||||
|
},
|
||||||
|
getIndices() {
|
||||||
|
return http({
|
||||||
|
url: `${indicesBasePath}/index_management/indices`,
|
||||||
|
method: 'GET',
|
||||||
|
}) as Promise<EsIndex[]>;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useApi = () => {
|
||||||
|
const appDeps = useAppDependencies();
|
||||||
|
|
||||||
|
const basePath = appDeps.core.http.basePath.prepend('/api/transform');
|
||||||
|
const indicesBasePath = appDeps.core.http.basePath.prepend('/api');
|
||||||
|
|
||||||
|
return apiFactory(basePath, indicesBasePath);
|
||||||
|
};
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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 { TransformId, TRANSFORM_STATE } from '../common';
|
||||||
|
|
||||||
|
export interface EsIndex {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransformEndpointRequest {
|
||||||
|
id: TransformId;
|
||||||
|
state?: TRANSFORM_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResultData {
|
||||||
|
success: boolean;
|
||||||
|
error?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransformEndpointResult {
|
||||||
|
[key: string]: ResultData;
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* 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 { toastNotifications } from 'ui/notify';
|
||||||
|
|
||||||
|
import { TransformListRow, refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common';
|
||||||
|
|
||||||
|
import { useApi } from './use_api';
|
||||||
|
import { TransformEndpointRequest, TransformEndpointResult } from './use_api_types';
|
||||||
|
|
||||||
|
export const useDeleteTransforms = () => {
|
||||||
|
const api = useApi();
|
||||||
|
|
||||||
|
return async (transforms: TransformListRow[]) => {
|
||||||
|
const transformsInfo: TransformEndpointRequest[] = transforms.map(tf => ({
|
||||||
|
id: tf.config.id,
|
||||||
|
state: tf.stats.state,
|
||||||
|
}));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const results: TransformEndpointResult = await api.deleteTransforms(transformsInfo);
|
||||||
|
for (const transformId in results) {
|
||||||
|
// hasOwnProperty check to ensure only properties on object itself, and not its prototypes
|
||||||
|
if (results.hasOwnProperty(transformId)) {
|
||||||
|
if (results[transformId].success === true) {
|
||||||
|
toastNotifications.addSuccess(
|
||||||
|
i18n.translate('xpack.transform.transformList.deleteTransformSuccessMessage', {
|
||||||
|
defaultMessage: 'Request to delete transform {transformId} acknowledged.',
|
||||||
|
values: { transformId },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
toastNotifications.addDanger(
|
||||||
|
i18n.translate('xpack.transform.transformList.deleteTransformErrorMessage', {
|
||||||
|
defaultMessage: 'An error occurred deleting the transform {transformId}',
|
||||||
|
values: { transformId },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH);
|
||||||
|
} catch (e) {
|
||||||
|
toastNotifications.addDanger(
|
||||||
|
i18n.translate('xpack.transform.transformList.deleteTransformGenericErrorMessage', {
|
||||||
|
defaultMessage: 'An error occurred calling the API endpoint to delete transforms.',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -4,31 +4,30 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ml } from '../../../../../services/ml_api_service';
|
|
||||||
import {
|
import {
|
||||||
DataFrameTransformListRow,
|
TransformListRow,
|
||||||
DataFrameTransformStats,
|
TransformStats,
|
||||||
DATA_FRAME_MODE,
|
TRANSFORM_MODE,
|
||||||
isDataFrameTransformStats,
|
isTransformStats,
|
||||||
DataFrameTransformPivotConfig,
|
TransformPivotConfig,
|
||||||
refreshTransformList$,
|
refreshTransformList$,
|
||||||
REFRESH_TRANSFORM_LIST_STATE,
|
REFRESH_TRANSFORM_LIST_STATE,
|
||||||
} from '../../../../common';
|
} from '../common';
|
||||||
|
|
||||||
interface GetDataFrameTransformsResponse {
|
import { useApi } from './use_api';
|
||||||
|
|
||||||
|
interface GetTransformsResponse {
|
||||||
count: number;
|
count: number;
|
||||||
transforms: DataFrameTransformPivotConfig[];
|
transforms: TransformPivotConfig[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GetDataFrameTransformsStatsResponseOk {
|
interface GetTransformsStatsResponseOk {
|
||||||
node_failures?: object;
|
node_failures?: object;
|
||||||
count: number;
|
count: number;
|
||||||
transforms: DataFrameTransformStats[];
|
transforms: TransformStats[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const isGetDataFrameTransformsStatsResponseOk = (
|
const isGetTransformsStatsResponseOk = (arg: any): arg is GetTransformsStatsResponseOk => {
|
||||||
arg: any
|
|
||||||
): arg is GetDataFrameTransformsStatsResponseOk => {
|
|
||||||
return (
|
return (
|
||||||
{}.hasOwnProperty.call(arg, 'count') &&
|
{}.hasOwnProperty.call(arg, 'count') &&
|
||||||
{}.hasOwnProperty.call(arg, 'transforms') &&
|
{}.hasOwnProperty.call(arg, 'transforms') &&
|
||||||
|
@ -36,26 +35,26 @@ const isGetDataFrameTransformsStatsResponseOk = (
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface GetDataFrameTransformsStatsResponseError {
|
interface GetTransformsStatsResponseError {
|
||||||
statusCode: number;
|
statusCode: number;
|
||||||
error: string;
|
error: string;
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetDataFrameTransformsStatsResponse =
|
type GetTransformsStatsResponse = GetTransformsStatsResponseOk | GetTransformsStatsResponseError;
|
||||||
| GetDataFrameTransformsStatsResponseOk
|
|
||||||
| GetDataFrameTransformsStatsResponseError;
|
|
||||||
|
|
||||||
export type GetTransforms = (forceRefresh?: boolean) => void;
|
export type GetTransforms = (forceRefresh?: boolean) => void;
|
||||||
|
|
||||||
export const getTransformsFactory = (
|
export const useGetTransforms = (
|
||||||
setTransforms: React.Dispatch<React.SetStateAction<DataFrameTransformListRow[]>>,
|
setTransforms: React.Dispatch<React.SetStateAction<TransformListRow[]>>,
|
||||||
setErrorMessage: React.Dispatch<
|
setErrorMessage: React.Dispatch<
|
||||||
React.SetStateAction<GetDataFrameTransformsStatsResponseError | undefined>
|
React.SetStateAction<GetTransformsStatsResponseError | undefined>
|
||||||
>,
|
>,
|
||||||
setIsInitialized: React.Dispatch<React.SetStateAction<boolean>>,
|
setIsInitialized: React.Dispatch<React.SetStateAction<boolean>>,
|
||||||
blockRefresh: boolean
|
blockRefresh: boolean
|
||||||
): GetTransforms => {
|
): GetTransforms => {
|
||||||
|
const api = useApi();
|
||||||
|
|
||||||
let concurrentLoads = 0;
|
let concurrentLoads = 0;
|
||||||
|
|
||||||
const getTransforms = async (forceRefresh = false) => {
|
const getTransforms = async (forceRefresh = false) => {
|
||||||
|
@ -68,18 +67,18 @@ export const getTransformsFactory = (
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const transformConfigs: GetDataFrameTransformsResponse = await ml.dataFrame.getDataFrameTransforms();
|
const transformConfigs: GetTransformsResponse = await api.getTransforms();
|
||||||
const transformStats: GetDataFrameTransformsStatsResponse = await ml.dataFrame.getDataFrameTransformsStats();
|
const transformStats: GetTransformsStatsResponse = await api.getTransformsStats();
|
||||||
|
|
||||||
const tableRows = transformConfigs.transforms.reduce(
|
const tableRows = transformConfigs.transforms.reduce(
|
||||||
(reducedtableRows, config) => {
|
(reducedtableRows, config) => {
|
||||||
const stats = isGetDataFrameTransformsStatsResponseOk(transformStats)
|
const stats = isGetTransformsStatsResponseOk(transformStats)
|
||||||
? transformStats.transforms.find(d => config.id === d.id)
|
? transformStats.transforms.find(d => config.id === d.id)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
// A newly created transform might not have corresponding stats yet.
|
// A newly created transform might not have corresponding stats yet.
|
||||||
// If that's the case we just skip the transform and don't add it to the transform list yet.
|
// If that's the case we just skip the transform and don't add it to the transform list yet.
|
||||||
if (!isDataFrameTransformStats(stats)) {
|
if (!isTransformStats(stats)) {
|
||||||
return reducedtableRows;
|
return reducedtableRows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,13 +88,13 @@ export const getTransformsFactory = (
|
||||||
config,
|
config,
|
||||||
mode:
|
mode:
|
||||||
typeof config.sync !== 'undefined'
|
typeof config.sync !== 'undefined'
|
||||||
? DATA_FRAME_MODE.CONTINUOUS
|
? TRANSFORM_MODE.CONTINUOUS
|
||||||
: DATA_FRAME_MODE.BATCH,
|
: TRANSFORM_MODE.BATCH,
|
||||||
stats,
|
stats,
|
||||||
});
|
});
|
||||||
return reducedtableRows;
|
return reducedtableRows;
|
||||||
},
|
},
|
||||||
[] as DataFrameTransformListRow[]
|
[] as TransformListRow[]
|
||||||
);
|
);
|
||||||
|
|
||||||
setTransforms(tableRows);
|
setTransforms(tableRows);
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* 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 { toastNotifications } from 'ui/notify';
|
||||||
|
|
||||||
|
import { TransformListRow, refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common';
|
||||||
|
|
||||||
|
import { useApi } from './use_api';
|
||||||
|
import { TransformEndpointRequest, TransformEndpointResult } from './use_api_types';
|
||||||
|
|
||||||
|
export const useStartTransforms = () => {
|
||||||
|
const api = useApi();
|
||||||
|
|
||||||
|
return async (transforms: TransformListRow[]) => {
|
||||||
|
const transformsInfo: TransformEndpointRequest[] = transforms.map(tf => ({
|
||||||
|
id: tf.config.id,
|
||||||
|
state: tf.stats.state,
|
||||||
|
}));
|
||||||
|
const results: TransformEndpointResult = await api.startTransforms(transformsInfo);
|
||||||
|
|
||||||
|
for (const transformId in results) {
|
||||||
|
// hasOwnProperty check to ensure only properties on object itself, and not its prototypes
|
||||||
|
if (results.hasOwnProperty(transformId)) {
|
||||||
|
if (results[transformId].success === true) {
|
||||||
|
toastNotifications.addSuccess(
|
||||||
|
i18n.translate('xpack.transform.transformList.startTransformSuccessMessage', {
|
||||||
|
defaultMessage: 'Request to start transform {transformId} acknowledged.',
|
||||||
|
values: { transformId },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
toastNotifications.addDanger(
|
||||||
|
i18n.translate('xpack.transform.transformList.startTransformErrorMessage', {
|
||||||
|
defaultMessage: 'An error occurred starting the transform {transformId}',
|
||||||
|
values: { transformId },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH);
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* 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 { toastNotifications } from 'ui/notify';
|
||||||
|
|
||||||
|
import { TransformListRow, refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common';
|
||||||
|
|
||||||
|
import { useApi } from './use_api';
|
||||||
|
import { TransformEndpointRequest, TransformEndpointResult } from './use_api_types';
|
||||||
|
|
||||||
|
export const useStopTransforms = () => {
|
||||||
|
const api = useApi();
|
||||||
|
|
||||||
|
return async (transforms: TransformListRow[]) => {
|
||||||
|
const transformsInfo: TransformEndpointRequest[] = transforms.map(df => ({
|
||||||
|
id: df.config.id,
|
||||||
|
state: df.stats.state,
|
||||||
|
}));
|
||||||
|
const results: TransformEndpointResult = await api.stopTransforms(transformsInfo);
|
||||||
|
|
||||||
|
for (const transformId in results) {
|
||||||
|
// hasOwnProperty check to ensure only properties on object itself, and not its prototypes
|
||||||
|
if (results.hasOwnProperty(transformId)) {
|
||||||
|
if (results[transformId].success === true) {
|
||||||
|
toastNotifications.addSuccess(
|
||||||
|
i18n.translate('xpack.transform.transformList.stopTransformSuccessMessage', {
|
||||||
|
defaultMessage: 'Request to stop data frame transform {transformId} acknowledged.',
|
||||||
|
values: { transformId },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
toastNotifications.addDanger(
|
||||||
|
i18n.translate('xpack.transform.transformList.stopTransformErrorMessage', {
|
||||||
|
defaultMessage: 'An error occurred stopping the data frame transform {transformId}',
|
||||||
|
values: { transformId },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH);
|
||||||
|
};
|
||||||
|
};
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue