mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -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/services/machine_learning/ @elastic/ml-ui
|
||||
/x-pack/test/functional/services/ml.ts @elastic/ml-ui
|
||||
/x-pack/legacy/plugins/transform/ @elastic/ml-ui
|
||||
|
||||
# Operations
|
||||
/renovate.json5 @elastic/kibana-operations
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
"xpack.server": "legacy/server",
|
||||
"xpack.snapshotRestore": "legacy/plugins/snapshot_restore",
|
||||
"xpack.spaces": "legacy/plugins/spaces",
|
||||
"xpack.transform": "legacy/plugins/transform",
|
||||
"xpack.upgradeAssistant": "legacy/plugins/upgrade_assistant",
|
||||
"xpack.uptime": "legacy/plugins/uptime",
|
||||
"xpack.watcher": "legacy/plugins/watcher"
|
||||
|
|
|
@ -41,6 +41,7 @@ import { fileUpload } from './legacy/plugins/file_upload';
|
|||
import { telemetry } from './legacy/plugins/telemetry';
|
||||
import { encryptedSavedObjects } from './legacy/plugins/encrypted_saved_objects';
|
||||
import { snapshotRestore } from './legacy/plugins/snapshot_restore';
|
||||
import { transform } from './legacy/plugins/transform';
|
||||
import { actions } from './legacy/plugins/actions';
|
||||
import { alerting } from './legacy/plugins/alerting';
|
||||
import { lens } from './legacy/plugins/lens';
|
||||
|
@ -75,6 +76,7 @@ module.exports = function (kibana) {
|
|||
infra(kibana),
|
||||
taskManager(kibana),
|
||||
rollup(kibana),
|
||||
transform(kibana),
|
||||
siem(kibana),
|
||||
remoteClusters(kibana),
|
||||
crossClusterReplication(kibana),
|
||||
|
|
|
@ -16,10 +16,6 @@ export interface AnalyticsMessage extends AuditMessageBase {
|
|||
analytics_id: string;
|
||||
}
|
||||
|
||||
export interface TransformMessage extends AuditMessageBase {
|
||||
transform_id: string;
|
||||
}
|
||||
|
||||
export interface JobMessage extends AuditMessageBase {
|
||||
job_id: string;
|
||||
}
|
||||
|
|
|
@ -28,12 +28,6 @@ export interface Privileges {
|
|||
canDeleteFilter: boolean;
|
||||
// File Data Visualizer
|
||||
canFindFileStructure: boolean;
|
||||
// Data Frame Transforms
|
||||
canGetDataFrame: boolean;
|
||||
canDeleteDataFrame: boolean;
|
||||
canPreviewDataFrame: boolean;
|
||||
canCreateDataFrame: boolean;
|
||||
canStartStopDataFrame: boolean;
|
||||
// Data Frame Analytics
|
||||
canGetDataFrameAnalytics: boolean;
|
||||
canDeleteDataFrameAnalytics: boolean;
|
||||
|
@ -65,12 +59,6 @@ export function getDefaultPrivileges(): Privileges {
|
|||
canDeleteFilter: false,
|
||||
// File Data Visualizer
|
||||
canFindFileStructure: false,
|
||||
// Data Frame Transforms
|
||||
canGetDataFrame: false,
|
||||
canDeleteDataFrame: false,
|
||||
canPreviewDataFrame: false,
|
||||
canCreateDataFrame: false,
|
||||
canStartStopDataFrame: false,
|
||||
// Data Frame Analytics
|
||||
canGetDataFrameAnalytics: 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;
|
||||
// it must also start and end with an alphanumeric character'
|
||||
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
|
||||
|
|
|
@ -20,7 +20,6 @@ import 'plugins/ml/jobs';
|
|||
import 'plugins/ml/overview';
|
||||
import 'plugins/ml/services/calendar_service';
|
||||
import 'plugins/ml/components/messagebar';
|
||||
import 'plugins/ml/data_frame';
|
||||
import 'plugins/ml/data_frame_analytics';
|
||||
import 'plugins/ml/datavisualizer';
|
||||
import 'plugins/ml/explorer';
|
||||
|
|
|
@ -17,28 +17,28 @@ interface Props {
|
|||
const [INFO, WARNING, ERROR] = ['info', 'warning', 'error'];
|
||||
|
||||
export const JobIcon: FC<Props> = ({ message, showTooltip = false }) => {
|
||||
if (message !== undefined) {
|
||||
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} />;
|
||||
}
|
||||
} else {
|
||||
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} />;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -37,13 +37,6 @@ function getTabs(disableLinks: boolean): Tab[] {
|
|||
}),
|
||||
disabled: disableLinks,
|
||||
},
|
||||
{
|
||||
id: 'data_frames',
|
||||
name: i18n.translate('xpack.ml.navMenu.dataFrameTabLinkText', {
|
||||
defaultMessage: 'Transforms',
|
||||
}),
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: 'data_frame_analytics',
|
||||
name: i18n.translate('xpack.ml.navMenu.dataFrameAnalyticsTabLinkText', {
|
||||
|
@ -68,7 +61,6 @@ interface TabData {
|
|||
const TAB_DATA: Record<TabId, TabData> = {
|
||||
overview: { testSubject: 'mlMainTab overview', pathId: 'overview' },
|
||||
anomaly_detection: { testSubject: 'mlMainTab anomalyDetection', pathId: 'jobs' },
|
||||
data_frames: { testSubject: 'mlMainTab dataFrames' },
|
||||
data_frame_analytics: { testSubject: 'mlMainTab dataFrameAnalytics' },
|
||||
datavisualizer: { testSubject: 'mlMainTab dataVisualizer' },
|
||||
};
|
||||
|
|
|
@ -21,7 +21,6 @@ const tabSupport: TabSupport = {
|
|||
overview: null,
|
||||
jobs: 'anomaly_detection',
|
||||
settings: 'anomaly_detection',
|
||||
data_frames: null,
|
||||
data_frame_analytics: null,
|
||||
datavisualizer: null,
|
||||
filedatavisualizer: null,
|
||||
|
|
|
@ -21,7 +21,6 @@ export function getTabs(tabId: TabId, disableLinks: boolean): Tab[] {
|
|||
const TAB_MAP: Partial<Record<TabId, Tab[]>> = {
|
||||
overview: [],
|
||||
datavisualizer: [],
|
||||
data_frames: [],
|
||||
data_frame_analytics: [],
|
||||
anomaly_detection: [
|
||||
{
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
.stat {
|
||||
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.
|
||||
*/
|
||||
|
||||
export {
|
||||
StatsBar,
|
||||
TransformStatsBarStats,
|
||||
AnalyticStatsBarStats,
|
||||
JobStatsBarStats,
|
||||
} from './stats_bar';
|
||||
export { StatsBar, AnalyticStatsBarStats, JobStatsBarStats } from './stats_bar';
|
||||
|
|
|
@ -18,7 +18,7 @@ interface StatProps {
|
|||
export const Stat: FC<StatProps> = ({ stat }) => {
|
||||
return (
|
||||
<span className="stat">
|
||||
<span>{stat.label}</span>: <span className="stat-value">{stat.value}</span>
|
||||
<span>{stat.label}</span>: <strong>{stat.value}</strong>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -18,18 +18,12 @@ export interface JobStatsBarStats extends Stats {
|
|||
activeDatafeeds: StatsBarStat;
|
||||
}
|
||||
|
||||
export interface TransformStatsBarStats extends Stats {
|
||||
batch: StatsBarStat;
|
||||
continuous: StatsBarStat;
|
||||
started: StatsBarStat;
|
||||
}
|
||||
|
||||
export interface AnalyticStatsBarStats extends Stats {
|
||||
started: StatsBarStat;
|
||||
stopped: StatsBarStat;
|
||||
}
|
||||
|
||||
type StatsBarStats = TransformStatsBarStats | JobStatsBarStats | AnalyticStatsBarStats;
|
||||
type StatsBarStats = JobStatsBarStats | AnalyticStatsBarStats;
|
||||
type StatsKey = keyof StatsBarStats;
|
||||
|
||||
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 (
|
||||
<EuiTabbedContent
|
||||
size="s"
|
||||
|
@ -110,6 +116,7 @@ export const ExpandedRow: FC<Props> = ({ item }) => {
|
|||
initialSelectedTab={tabs[0]}
|
||||
onTabClick={() => {}}
|
||||
expand={false}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
@import 'app';
|
||||
|
||||
// Sub applications
|
||||
@import 'data_frame/index';
|
||||
@import 'data_frame_analytics/index';
|
||||
@import 'datavisualizer/index';
|
||||
@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.
|
||||
// takes the name of the privilege variable as specified in get_privileges.js
|
||||
export function checkPermission(privilegeType: keyof Privileges) {
|
||||
|
@ -168,21 +132,6 @@ export function createPermissionFailureMessage(privilegeType: keyof Privileges)
|
|||
message = i18n.translate('xpack.ml.privilege.noPermission.runForecastsTooltip', {
|
||||
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', {
|
||||
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 { ExistingJobsAndGroups } from '../job_service';
|
||||
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';
|
||||
|
||||
// TODO This is not a complete representation of all methods of `ml.*`.
|
||||
|
@ -48,23 +44,6 @@ declare interface Ml {
|
|||
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>;
|
||||
|
||||
checkMlPrivileges(): Promise<PrivilegesResponse>;
|
||||
|
|
|
@ -12,7 +12,6 @@ import chrome from 'ui/chrome';
|
|||
import { http } from '../../services/http_service';
|
||||
|
||||
import { annotations } from './annotations';
|
||||
import { dataFrame } from './data_frame';
|
||||
import { dataFrameAnalytics } from './data_frame_analytics';
|
||||
import { filters } from './filters';
|
||||
import { results } from './results';
|
||||
|
@ -450,7 +449,6 @@ export const ml = {
|
|||
},
|
||||
|
||||
annotations,
|
||||
dataFrame,
|
||||
dataFrameAnalytics,
|
||||
filters,
|
||||
results,
|
||||
|
|
|
@ -205,119 +205,6 @@ export const elasticsearchJsPlugin = (Client, config, components) => {
|
|||
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({
|
||||
urls: [
|
||||
{
|
||||
|
|
|
@ -105,27 +105,19 @@ const fullClusterPrivileges = {
|
|||
'cluster:monitor/xpack/ml/calendars/get': true,
|
||||
'cluster:admin/xpack/ml/filters/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/calendars/events/post': true,
|
||||
'cluster:monitor/data_frame/get': true,
|
||||
'cluster:admin/xpack/ml/job/close': 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/data_frame/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/datafeeds/put': true,
|
||||
'cluster:admin/xpack/ml/job/open': true,
|
||||
'cluster:admin/xpack/ml/job/delete': 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/update': true,
|
||||
'cluster:admin/data_frame/start': true,
|
||||
'cluster:admin/xpack/ml/calendars/delete': true,
|
||||
'cluster:monitor/xpack/ml/findfilestructure': true,
|
||||
};
|
||||
|
@ -144,27 +136,19 @@ const partialClusterPrivileges = {
|
|||
'cluster:monitor/xpack/ml/calendars/get': true,
|
||||
'cluster:admin/xpack/ml/filters/get': false,
|
||||
'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/calendars/events/post': false,
|
||||
'cluster:monitor/data_frame/get': false,
|
||||
'cluster:admin/xpack/ml/job/close': false,
|
||||
'cluster:monitor/xpack/ml/datafeeds/stats/get': true,
|
||||
'cluster:admin/data_frame/preview': false,
|
||||
'cluster:admin/xpack/ml/calendars/jobs/update': false,
|
||||
'cluster:admin/data_frame/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/datafeeds/put': false,
|
||||
'cluster:admin/xpack/ml/job/open': false,
|
||||
'cluster:admin/xpack/ml/job/delete': false,
|
||||
'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/update': false,
|
||||
'cluster:admin/data_frame/start': false,
|
||||
'cluster:admin/xpack/ml/calendars/delete': false,
|
||||
'cluster:monitor/xpack/ml/findfilestructure': true,
|
||||
};
|
||||
|
|
|
@ -91,7 +91,7 @@ describe('check_privileges', () => {
|
|||
describe('getPrivileges() - right number of capabilities', () => {
|
||||
test('es capabilities count', async done => {
|
||||
const count = mlPrivileges.cluster.length;
|
||||
expect(count).toBe(35);
|
||||
expect(count).toBe(27);
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -104,7 +104,7 @@ describe('check_privileges', () => {
|
|||
);
|
||||
const { capabilities } = await getPrivileges();
|
||||
const count = Object.keys(capabilities).length;
|
||||
expect(count).toBe(27);
|
||||
expect(count).toBe(22);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -138,11 +138,6 @@ describe('check_privileges', () => {
|
|||
expect(capabilities.canCreateFilter).toBe(false);
|
||||
expect(capabilities.canDeleteFilter).toBe(false);
|
||||
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.canDeleteDataFrameAnalytics).toBe(false);
|
||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||
|
@ -178,11 +173,6 @@ describe('check_privileges', () => {
|
|||
expect(capabilities.canCreateFilter).toBe(true);
|
||||
expect(capabilities.canDeleteFilter).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.canDeleteDataFrameAnalytics).toBe(true);
|
||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(true);
|
||||
|
@ -218,11 +208,6 @@ describe('check_privileges', () => {
|
|||
expect(capabilities.canCreateFilter).toBe(false);
|
||||
expect(capabilities.canDeleteFilter).toBe(false);
|
||||
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.canDeleteDataFrameAnalytics).toBe(false);
|
||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||
|
@ -258,11 +243,6 @@ describe('check_privileges', () => {
|
|||
expect(capabilities.canCreateFilter).toBe(false);
|
||||
expect(capabilities.canDeleteFilter).toBe(false);
|
||||
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.canDeleteDataFrameAnalytics).toBe(false);
|
||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||
|
@ -298,11 +278,6 @@ describe('check_privileges', () => {
|
|||
expect(capabilities.canCreateFilter).toBe(false);
|
||||
expect(capabilities.canDeleteFilter).toBe(false);
|
||||
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.canDeleteDataFrameAnalytics).toBe(false);
|
||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||
|
@ -338,11 +313,6 @@ describe('check_privileges', () => {
|
|||
expect(capabilities.canCreateFilter).toBe(false);
|
||||
expect(capabilities.canDeleteFilter).toBe(false);
|
||||
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.canDeleteDataFrameAnalytics).toBe(false);
|
||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||
|
@ -378,11 +348,6 @@ describe('check_privileges', () => {
|
|||
expect(capabilities.canCreateFilter).toBe(false);
|
||||
expect(capabilities.canDeleteFilter).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.canDeleteDataFrameAnalytics).toBe(false);
|
||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||
|
@ -420,11 +385,6 @@ describe('check_privileges', () => {
|
|||
expect(capabilities.canCreateFilter).toBe(true);
|
||||
expect(capabilities.canDeleteFilter).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.canDeleteDataFrameAnalytics).toBe(true);
|
||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(true);
|
||||
|
@ -460,11 +420,6 @@ describe('check_privileges', () => {
|
|||
expect(capabilities.canCreateFilter).toBe(false);
|
||||
expect(capabilities.canDeleteFilter).toBe(false);
|
||||
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.canDeleteDataFrameAnalytics).toBe(false);
|
||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||
|
@ -500,11 +455,6 @@ describe('check_privileges', () => {
|
|||
expect(capabilities.canCreateFilter).toBe(false);
|
||||
expect(capabilities.canDeleteFilter).toBe(false);
|
||||
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.canDeleteDataFrameAnalytics).toBe(false);
|
||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||
|
@ -540,11 +490,6 @@ describe('check_privileges', () => {
|
|||
expect(capabilities.canCreateFilter).toBe(false);
|
||||
expect(capabilities.canDeleteFilter).toBe(false);
|
||||
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.canDeleteDataFrameAnalytics).toBe(false);
|
||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||
|
@ -580,11 +525,6 @@ describe('check_privileges', () => {
|
|||
expect(capabilities.canCreateFilter).toBe(false);
|
||||
expect(capabilities.canDeleteFilter).toBe(false);
|
||||
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.canDeleteDataFrameAnalytics).toBe(false);
|
||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||
|
@ -620,11 +560,6 @@ describe('check_privileges', () => {
|
|||
expect(capabilities.canCreateFilter).toBe(false);
|
||||
expect(capabilities.canDeleteFilter).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.canDeleteDataFrameAnalytics).toBe(false);
|
||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||
|
|
|
@ -129,14 +129,6 @@ function setFullGettingPrivileges(
|
|||
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
|
||||
if (
|
||||
forceTrue ||
|
||||
|
@ -234,28 +226,6 @@ function setFullActionPrivileges(
|
|||
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
|
||||
if (
|
||||
forceTrue ||
|
||||
|
@ -293,40 +263,10 @@ function setBasicGettingPrivileges(
|
|||
if (forceTrue || cluster['cluster:monitor/xpack/ml/findfilestructure']) {
|
||||
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(
|
||||
cluster: ClusterPrivilege = {},
|
||||
privileges: Privileges,
|
||||
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 = {
|
||||
cluster: [
|
||||
'cluster:monitor/data_frame/get',
|
||||
'cluster:monitor/data_frame/stats/get',
|
||||
'cluster:monitor/xpack/ml/job/get',
|
||||
'cluster:monitor/xpack/ml/job/stats/get',
|
||||
'cluster:monitor/xpack/ml/datafeeds/get',
|
||||
'cluster:monitor/xpack/ml/datafeeds/stats/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/delete',
|
||||
'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
|
||||
import { systemRoutes } from '../routes/system';
|
||||
// @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';
|
||||
// @ts-ignore: could not find declaration file for module
|
||||
import { dataRecognizer } from '../routes/modules';
|
||||
|
@ -222,7 +220,6 @@ export class Plugin {
|
|||
annotationRoutes(routeInitializationDeps);
|
||||
jobRoutes(routeInitializationDeps);
|
||||
dataFeedRoutes(routeInitializationDeps);
|
||||
dataFrameRoutes(routeInitializationDeps);
|
||||
dataFrameAnalyticsRoutes(routeInitializationDeps);
|
||||
indicesRoutes(routeInitializationDeps);
|
||||
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';
|
||||
|
||||
describe('Data Frame: Aggregations', () => {
|
||||
describe('Transform: Aggregations', () => {
|
||||
test('isAggName()', () => {
|
||||
expect(isAggName('avg(responsetime)')).toEqual(true);
|
||||
expect(isAggName('avg_responsetime')).toEqual(true);
|
|
@ -22,22 +22,22 @@ export {
|
|||
useRefreshTransformList,
|
||||
CreateRequestBody,
|
||||
PreviewRequestBody,
|
||||
DataFrameTransformId,
|
||||
DataFrameTransformPivotConfig,
|
||||
TransformId,
|
||||
TransformPivotConfig,
|
||||
IndexName,
|
||||
IndexPattern,
|
||||
REFRESH_TRANSFORM_LIST_STATE,
|
||||
} from './transform';
|
||||
export { DATA_FRAME_TRANSFORM_LIST_COLUMN, DataFrameTransformListRow } from './transform_list';
|
||||
export { TRANSFORM_LIST_COLUMN, TransformListRow } from './transform_list';
|
||||
export {
|
||||
getTransformProgress,
|
||||
isCompletedBatchTransform,
|
||||
isDataFrameTransformStats,
|
||||
DataFrameTransformStats,
|
||||
DATA_FRAME_MODE,
|
||||
DATA_FRAME_TRANSFORM_STATE,
|
||||
isTransformStats,
|
||||
TransformStats,
|
||||
TRANSFORM_MODE,
|
||||
TRANSFORM_STATE,
|
||||
} from './transform_stats';
|
||||
export { moveToDataFrameWizard, getDiscoverUrl } from './navigation';
|
||||
export { getDiscoverUrl } from './navigation';
|
||||
export {
|
||||
getEsAggFromAggConfig,
|
||||
isPivotAggsConfigWithUiSupport,
|
|
@ -4,15 +4,15 @@
|
|||
* 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';
|
||||
|
||||
export function moveToDataFrameWizard() {
|
||||
window.location.href = '#/data_frames/new_transform';
|
||||
}
|
||||
import { CLIENT_BASE_PATH, SECTION_SLUG } from '../constants';
|
||||
|
||||
/**
|
||||
* Gets a url for navigating to Discover page.
|
||||
* @param indexPatternId Index pattern id.
|
||||
* @param indexPatternId Index pattern ID.
|
||||
* @param baseUrl Base url.
|
||||
*/
|
||||
export function getDiscoverUrl(indexPatternId: string, baseUrl: string): string {
|
||||
|
@ -27,3 +27,11 @@ export function getDiscoverUrl(indexPatternId: string, baseUrl: string): string
|
|||
|
||||
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 { StepDefineExposedState } from '../pages/data_frame_new_pivot/components/step_define/step_define_form';
|
||||
import { StepDetailsExposedState } from '../pages/data_frame_new_pivot/components/step_details/step_details_form';
|
||||
import { StepDefineExposedState } from '../sections/create_transform/components/step_define/step_define_form';
|
||||
import { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form';
|
||||
|
||||
import { PIVOT_SUPPORTED_GROUP_BY_AGGS } from './pivot_group_by';
|
||||
import { PivotAggsConfig, PIVOT_SUPPORTED_AGGS } from './pivot_aggs';
|
||||
|
@ -24,7 +24,7 @@ const defaultQuery: PivotQuery = { query_string: { query: '*' } };
|
|||
const matchAllQuery: PivotQuery = { match_all: {} };
|
||||
const simpleQuery: PivotQuery = { query_string: { query: 'airline:AAL' } };
|
||||
|
||||
describe('Data Frame: Common', () => {
|
||||
describe('Transform: Common', () => {
|
||||
test('isSimpleQuery()', () => {
|
||||
expect(isSimpleQuery(defaultQuery)).toBe(true);
|
||||
expect(isSimpleQuery(matchAllQuery)).toBe(false);
|
|
@ -9,10 +9,10 @@ import { DefaultOperator } from 'elasticsearch';
|
|||
import { IndexPattern } from 'ui/index_patterns';
|
||||
|
||||
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 { StepDetailsExposedState } from '../pages/data_frame_new_pivot/components/step_details/step_details_form';
|
||||
import { StepDefineExposedState } from '../sections/create_transform/components/step_define/step_define_form';
|
||||
import { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form';
|
||||
|
||||
import {
|
||||
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 { Subscription } from 'rxjs';
|
||||
|
||||
// @ts-ignore
|
||||
import { isJobIdValid } from '../../../common/util/job_utils';
|
||||
|
||||
import { PivotAggDict } from './pivot_aggs';
|
||||
import { PivotGroupByDict } from './pivot_group_by';
|
||||
|
||||
export const isTransformIdValid = isJobIdValid;
|
||||
|
||||
export type IndexName = 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 {
|
||||
pivot: {
|
||||
|
@ -45,8 +46,8 @@ export interface CreateRequestBody extends PreviewRequestBody {
|
|||
};
|
||||
}
|
||||
|
||||
export interface DataFrameTransformPivotConfig extends CreateRequestBody {
|
||||
id: DataFrameTransformId;
|
||||
export interface TransformPivotConfig extends CreateRequestBody {
|
||||
id: TransformId;
|
||||
}
|
||||
|
||||
export enum REFRESH_TRANSFORM_LIST_STATE {
|
|
@ -4,20 +4,20 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { DataFrameTransformId, DataFrameTransformPivotConfig } from './transform';
|
||||
import { DataFrameTransformStats } from './transform_stats';
|
||||
import { TransformId, TransformPivotConfig } from './transform';
|
||||
import { TransformStats } from './transform_stats';
|
||||
|
||||
// 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_SOURCE_INDEX = 'config.source.index',
|
||||
DESCRIPTION = 'config.description',
|
||||
ID = 'id',
|
||||
}
|
||||
|
||||
export interface DataFrameTransformListRow {
|
||||
id: DataFrameTransformId;
|
||||
config: DataFrameTransformPivotConfig;
|
||||
export interface TransformListRow {
|
||||
id: TransformId;
|
||||
config: TransformPivotConfig;
|
||||
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.
|
||||
*/
|
||||
|
||||
import mockDataFrameTransformListRow from './__mocks__/data_frame_transform_list_row.json';
|
||||
import mockDataFrameTransformStats from './__mocks__/data_frame_transform_stats.json';
|
||||
import mockTransformListRow from './__mocks__/transform_list_row.json';
|
||||
import mockTransformStats from './__mocks__/transform_stats.json';
|
||||
|
||||
import { DataFrameTransformListRow } from './transform_list';
|
||||
import { TransformListRow } from './transform_list';
|
||||
import { getTransformProgress, isCompletedBatchTransform } from './transform_stats';
|
||||
|
||||
const getRow = (statsId: string) => {
|
||||
return {
|
||||
...(mockDataFrameTransformListRow as DataFrameTransformListRow),
|
||||
...(mockTransformListRow as TransformListRow),
|
||||
stats: {
|
||||
...mockDataFrameTransformStats.transforms.find(
|
||||
(stats: DataFrameTransformListRow['stats']) => stats.id === statsId
|
||||
...mockTransformStats.transforms.find(
|
||||
(stats: TransformListRow['stats']) => stats.id === statsId
|
||||
),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
describe('Data Frame: Transform stats.', () => {
|
||||
describe('Transform: Transform stats.', () => {
|
||||
test('getTransformProgress()', () => {
|
||||
// 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.
|
|
@ -6,11 +6,11 @@
|
|||
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
|
||||
import { DataFrameTransformId } from './transform';
|
||||
import { DataFrameTransformListRow } from './transform_list';
|
||||
import { TransformId } from './transform';
|
||||
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
|
||||
export enum DATA_FRAME_TRANSFORM_STATE {
|
||||
export enum TRANSFORM_STATE {
|
||||
ABORTING = 'aborting',
|
||||
FAILED = 'failed',
|
||||
INDEXING = 'indexing',
|
||||
|
@ -19,13 +19,13 @@ export enum DATA_FRAME_TRANSFORM_STATE {
|
|||
STOPPING = 'stopping',
|
||||
}
|
||||
|
||||
export enum DATA_FRAME_MODE {
|
||||
export enum TRANSFORM_MODE {
|
||||
BATCH = 'batch',
|
||||
CONTINUOUS = 'continuous',
|
||||
}
|
||||
|
||||
export interface DataFrameTransformStats {
|
||||
id: DataFrameTransformId;
|
||||
export interface TransformStats {
|
||||
id: TransformId;
|
||||
checkpointing: {
|
||||
last: {
|
||||
checkpoint: number;
|
||||
|
@ -61,19 +61,19 @@ export interface DataFrameTransformStats {
|
|||
trigger_count: number;
|
||||
};
|
||||
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 (
|
||||
typeof arg === 'object' &&
|
||||
arg !== null &&
|
||||
{}.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)) {
|
||||
return 100;
|
||||
}
|
||||
|
@ -83,12 +83,12 @@ export function getTransformProgress(item: DataFrameTransformListRow) {
|
|||
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,
|
||||
// then this is a completed batch data frame transform.
|
||||
// then this is a completed batch transform.
|
||||
return (
|
||||
item.stats.checkpointing.last.checkpoint === 1 &&
|
||||
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.
|
||||
*/
|
||||
|
||||
export { getTransformsFactory } from './get_transforms';
|
||||
export { deleteTransforms } from './delete_transform';
|
||||
export { startTransforms } from './start_transform';
|
||||
export { stopTransforms } from './stop_transform';
|
||||
export { SectionError } from './section_error';
|
||||
export { SectionLoading } from './section_loading';
|
|
@ -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.
|
||||
*/
|
||||
|
||||
import { ml } from '../../../../../services/ml_api_service';
|
||||
import {
|
||||
DataFrameTransformListRow,
|
||||
DataFrameTransformStats,
|
||||
DATA_FRAME_MODE,
|
||||
isDataFrameTransformStats,
|
||||
DataFrameTransformPivotConfig,
|
||||
TransformListRow,
|
||||
TransformStats,
|
||||
TRANSFORM_MODE,
|
||||
isTransformStats,
|
||||
TransformPivotConfig,
|
||||
refreshTransformList$,
|
||||
REFRESH_TRANSFORM_LIST_STATE,
|
||||
} from '../../../../common';
|
||||
} from '../common';
|
||||
|
||||
interface GetDataFrameTransformsResponse {
|
||||
import { useApi } from './use_api';
|
||||
|
||||
interface GetTransformsResponse {
|
||||
count: number;
|
||||
transforms: DataFrameTransformPivotConfig[];
|
||||
transforms: TransformPivotConfig[];
|
||||
}
|
||||
|
||||
interface GetDataFrameTransformsStatsResponseOk {
|
||||
interface GetTransformsStatsResponseOk {
|
||||
node_failures?: object;
|
||||
count: number;
|
||||
transforms: DataFrameTransformStats[];
|
||||
transforms: TransformStats[];
|
||||
}
|
||||
|
||||
const isGetDataFrameTransformsStatsResponseOk = (
|
||||
arg: any
|
||||
): arg is GetDataFrameTransformsStatsResponseOk => {
|
||||
const isGetTransformsStatsResponseOk = (arg: any): arg is GetTransformsStatsResponseOk => {
|
||||
return (
|
||||
{}.hasOwnProperty.call(arg, 'count') &&
|
||||
{}.hasOwnProperty.call(arg, 'transforms') &&
|
||||
|
@ -36,26 +35,26 @@ const isGetDataFrameTransformsStatsResponseOk = (
|
|||
);
|
||||
};
|
||||
|
||||
interface GetDataFrameTransformsStatsResponseError {
|
||||
interface GetTransformsStatsResponseError {
|
||||
statusCode: number;
|
||||
error: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
type GetDataFrameTransformsStatsResponse =
|
||||
| GetDataFrameTransformsStatsResponseOk
|
||||
| GetDataFrameTransformsStatsResponseError;
|
||||
type GetTransformsStatsResponse = GetTransformsStatsResponseOk | GetTransformsStatsResponseError;
|
||||
|
||||
export type GetTransforms = (forceRefresh?: boolean) => void;
|
||||
|
||||
export const getTransformsFactory = (
|
||||
setTransforms: React.Dispatch<React.SetStateAction<DataFrameTransformListRow[]>>,
|
||||
export const useGetTransforms = (
|
||||
setTransforms: React.Dispatch<React.SetStateAction<TransformListRow[]>>,
|
||||
setErrorMessage: React.Dispatch<
|
||||
React.SetStateAction<GetDataFrameTransformsStatsResponseError | undefined>
|
||||
React.SetStateAction<GetTransformsStatsResponseError | undefined>
|
||||
>,
|
||||
setIsInitialized: React.Dispatch<React.SetStateAction<boolean>>,
|
||||
blockRefresh: boolean
|
||||
): GetTransforms => {
|
||||
const api = useApi();
|
||||
|
||||
let concurrentLoads = 0;
|
||||
|
||||
const getTransforms = async (forceRefresh = false) => {
|
||||
|
@ -68,18 +67,18 @@ export const getTransformsFactory = (
|
|||
}
|
||||
|
||||
try {
|
||||
const transformConfigs: GetDataFrameTransformsResponse = await ml.dataFrame.getDataFrameTransforms();
|
||||
const transformStats: GetDataFrameTransformsStatsResponse = await ml.dataFrame.getDataFrameTransformsStats();
|
||||
const transformConfigs: GetTransformsResponse = await api.getTransforms();
|
||||
const transformStats: GetTransformsStatsResponse = await api.getTransformsStats();
|
||||
|
||||
const tableRows = transformConfigs.transforms.reduce(
|
||||
(reducedtableRows, config) => {
|
||||
const stats = isGetDataFrameTransformsStatsResponseOk(transformStats)
|
||||
const stats = isGetTransformsStatsResponseOk(transformStats)
|
||||
? transformStats.transforms.find(d => config.id === d.id)
|
||||
: undefined;
|
||||
|
||||
// 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 (!isDataFrameTransformStats(stats)) {
|
||||
if (!isTransformStats(stats)) {
|
||||
return reducedtableRows;
|
||||
}
|
||||
|
||||
|
@ -89,13 +88,13 @@ export const getTransformsFactory = (
|
|||
config,
|
||||
mode:
|
||||
typeof config.sync !== 'undefined'
|
||||
? DATA_FRAME_MODE.CONTINUOUS
|
||||
: DATA_FRAME_MODE.BATCH,
|
||||
? TRANSFORM_MODE.CONTINUOUS
|
||||
: TRANSFORM_MODE.BATCH,
|
||||
stats,
|
||||
});
|
||||
return reducedtableRows;
|
||||
},
|
||||
[] as DataFrameTransformListRow[]
|
||||
[] as TransformListRow[]
|
||||
);
|
||||
|
||||
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