mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* [ML] Add tests to create transforms (#49760) * Apply #51066 to backport * Adjust eui switch handling for 7.5
This commit is contained in:
parent
aef43e72a7
commit
785807f676
36 changed files with 1715 additions and 705 deletions
|
@ -178,6 +178,7 @@ export class FilterBar extends Component {
|
|||
onClick={this.onClickInput}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
data-test-subj={this.props.testSubj}
|
||||
/>
|
||||
|
||||
{this.props.isLoading && (
|
||||
|
@ -213,12 +214,14 @@ FilterBar.propTypes = {
|
|||
placeholder: PropTypes.string,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
valueExternal: PropTypes.string,
|
||||
suggestions: PropTypes.array.isRequired
|
||||
suggestions: PropTypes.array.isRequired,
|
||||
testSubj: PropTypes.string,
|
||||
};
|
||||
|
||||
FilterBar.defaultProps = {
|
||||
isLoading: false,
|
||||
disabled: false,
|
||||
placeholder: 'tag : engineering OR tag : marketing',
|
||||
suggestions: []
|
||||
suggestions: [],
|
||||
testSubj: undefined,
|
||||
};
|
||||
|
|
|
@ -90,7 +90,7 @@ export class KqlFilterBar extends Component {
|
|||
|
||||
render() {
|
||||
const { error } = this.state;
|
||||
const { initialValue, placeholder, valueExternal } = this.props;
|
||||
const { initialValue, placeholder, valueExternal, testSubj } = this.props;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
@ -103,6 +103,7 @@ export class KqlFilterBar extends Component {
|
|||
onSubmit={this.onSubmit}
|
||||
suggestions={this.state.suggestions}
|
||||
valueExternal={valueExternal}
|
||||
testSubj={testSubj}
|
||||
/>
|
||||
{ error &&
|
||||
<EuiCallOut color="danger">
|
||||
|
@ -118,6 +119,7 @@ KqlFilterBar.propTypes = {
|
|||
initialValue: PropTypes.string,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
placeholder: PropTypes.string,
|
||||
valueExternal: PropTypes.string
|
||||
valueExternal: PropTypes.string,
|
||||
testSubj: PropTypes.string,
|
||||
};
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ export interface FieldDataColumnType {
|
|||
render?: RenderFunc;
|
||||
footer?: string | ReactElement | FooterFunc;
|
||||
textOnly?: boolean;
|
||||
'data-test-subj'?: string;
|
||||
}
|
||||
|
||||
export interface ComputedColumnType {
|
||||
|
@ -40,6 +41,7 @@ export interface ComputedColumnType {
|
|||
sortable?: (item: Item) => any;
|
||||
width?: string;
|
||||
truncateText?: boolean;
|
||||
'data-test-subj'?: string;
|
||||
}
|
||||
|
||||
type ICON_TYPES = any;
|
||||
|
@ -183,7 +185,7 @@ export type EuiInMemoryTableProps = CommonProps & {
|
|||
selection?: SelectionType;
|
||||
itemId?: ItemIdType;
|
||||
itemIdToExpandedRowMap?: Record<string, Item>;
|
||||
rowProps?: () => void | Record<string, any>;
|
||||
rowProps?: (item: Item) => void | Record<string, any>;
|
||||
cellProps?: () => void | Record<string, any>;
|
||||
onTableChange?: (arg: OnTableChangeArg) => void;
|
||||
};
|
||||
|
|
|
@ -12,12 +12,14 @@ interface Props {
|
|||
options: EuiComboBoxOptionProps[];
|
||||
placeholder?: string;
|
||||
changeHandler(d: EuiComboBoxOptionProps[]): void;
|
||||
testSubj?: string;
|
||||
}
|
||||
|
||||
export const DropDown: React.SFC<Props> = ({
|
||||
changeHandler,
|
||||
options,
|
||||
placeholder = 'Search ...',
|
||||
testSubj,
|
||||
}) => {
|
||||
return (
|
||||
<EuiComboBox
|
||||
|
@ -27,6 +29,7 @@ export const DropDown: React.SFC<Props> = ({
|
|||
selectedOptions={[]}
|
||||
onChange={changeHandler}
|
||||
isClearable={false}
|
||||
data-test-subj={testSubj}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -11,6 +11,7 @@ exports[`Transform: <AggLabelForm /> Date histogram aggregation 1`] = `
|
|||
>
|
||||
<span
|
||||
className="eui-textTruncate"
|
||||
data-test-subj="transformAggregationEntryLabel"
|
||||
>
|
||||
the-group-by-agg-name
|
||||
</span>
|
||||
|
@ -24,6 +25,7 @@ exports[`Transform: <AggLabelForm /> Date histogram aggregation 1`] = `
|
|||
button={
|
||||
<EuiButtonIcon
|
||||
aria-label="Edit aggregation"
|
||||
data-test-subj="transformAggregationEntryEditButton"
|
||||
iconType="pencil"
|
||||
onClick={[Function]}
|
||||
size="s"
|
||||
|
@ -58,6 +60,7 @@ exports[`Transform: <AggLabelForm /> Date histogram aggregation 1`] = `
|
|||
>
|
||||
<EuiButtonIcon
|
||||
aria-label="Delete item"
|
||||
data-test-subj="transformAggregationEntryDeleteButton"
|
||||
iconType="cross"
|
||||
onClick={[Function]}
|
||||
size="s"
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
exports[`Transform: <AggListForm /> Minimal initialization 1`] = `
|
||||
<Fragment>
|
||||
<EuiPanel
|
||||
data-test-subj="transformAggregationEntry 0"
|
||||
paddingSize="s"
|
||||
>
|
||||
<AggLabelForm
|
||||
|
|
|
@ -39,7 +39,9 @@ export const AggLabelForm: React.SFC<Props> = ({
|
|||
return (
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false}>
|
||||
<EuiFlexItem className="transform__AggregationLabel--text">
|
||||
<span className="eui-textTruncate">{item.aggName}</span>
|
||||
<span className="eui-textTruncate" data-test-subj="transformAggregationEntryLabel">
|
||||
{item.aggName}
|
||||
</span>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false} className="transform__GroupByLabel--button">
|
||||
<EuiPopover
|
||||
|
@ -53,6 +55,7 @@ export const AggLabelForm: React.SFC<Props> = ({
|
|||
size="s"
|
||||
iconType="pencil"
|
||||
onClick={() => setPopoverVisibility(!isPopoverVisible)}
|
||||
data-test-subj="transformAggregationEntryEditButton"
|
||||
/>
|
||||
}
|
||||
isOpen={isPopoverVisible}
|
||||
|
@ -74,6 +77,7 @@ export const AggLabelForm: React.SFC<Props> = ({
|
|||
size="s"
|
||||
iconType="cross"
|
||||
onClick={() => deleteHandler(item.aggName)}
|
||||
data-test-subj="transformAggregationEntryDeleteButton"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -33,11 +33,11 @@ export const AggListForm: React.SFC<AggListProps> = ({
|
|||
const listKeys = Object.keys(list);
|
||||
return (
|
||||
<Fragment>
|
||||
{listKeys.map((aggName: AggName) => {
|
||||
{listKeys.map((aggName: AggName, i) => {
|
||||
const otherAggNames = listKeys.filter(k => k !== aggName);
|
||||
return (
|
||||
<Fragment key={aggName}>
|
||||
<EuiPanel paddingSize="s">
|
||||
<EuiPanel paddingSize="s" data-test-subj={`transformAggregationEntry ${i}`}>
|
||||
<AggLabelForm
|
||||
deleteHandler={deleteHandler}
|
||||
item={list[aggName]}
|
||||
|
|
|
@ -11,6 +11,7 @@ exports[`Transform: <GroupByLabelForm /> Date histogram aggregation 1`] = `
|
|||
>
|
||||
<span
|
||||
className="eui-textTruncate"
|
||||
data-test-subj="transformGroupByEntryLabel"
|
||||
>
|
||||
the-group-by-agg-name
|
||||
</span>
|
||||
|
@ -22,6 +23,7 @@ exports[`Transform: <GroupByLabelForm /> Date histogram aggregation 1`] = `
|
|||
<EuiTextColor
|
||||
className="eui-textTruncate"
|
||||
color="subdued"
|
||||
data-test-subj="transformGroupByEntryIntervalLabel"
|
||||
>
|
||||
1m
|
||||
</EuiTextColor>
|
||||
|
@ -35,6 +37,7 @@ exports[`Transform: <GroupByLabelForm /> Date histogram aggregation 1`] = `
|
|||
button={
|
||||
<EuiButtonIcon
|
||||
aria-label="Edit interval"
|
||||
data-test-subj="transformGroupByEntryEditButton"
|
||||
iconType="pencil"
|
||||
onClick={[Function]}
|
||||
size="s"
|
||||
|
@ -70,6 +73,7 @@ exports[`Transform: <GroupByLabelForm /> Date histogram aggregation 1`] = `
|
|||
>
|
||||
<EuiButtonIcon
|
||||
aria-label="Delete item"
|
||||
data-test-subj="transformGroupByEntryDeleteButton"
|
||||
iconType="cross"
|
||||
onClick={[Function]}
|
||||
size="s"
|
||||
|
@ -89,6 +93,7 @@ exports[`Transform: <GroupByLabelForm /> Histogram aggregation 1`] = `
|
|||
>
|
||||
<span
|
||||
className="eui-textTruncate"
|
||||
data-test-subj="transformGroupByEntryLabel"
|
||||
>
|
||||
the-group-by-agg-name
|
||||
</span>
|
||||
|
@ -100,6 +105,7 @@ exports[`Transform: <GroupByLabelForm /> Histogram aggregation 1`] = `
|
|||
<EuiTextColor
|
||||
className="eui-textTruncate"
|
||||
color="subdued"
|
||||
data-test-subj="transformGroupByEntryIntervalLabel"
|
||||
>
|
||||
100
|
||||
</EuiTextColor>
|
||||
|
@ -113,6 +119,7 @@ exports[`Transform: <GroupByLabelForm /> Histogram aggregation 1`] = `
|
|||
button={
|
||||
<EuiButtonIcon
|
||||
aria-label="Edit interval"
|
||||
data-test-subj="transformGroupByEntryEditButton"
|
||||
iconType="pencil"
|
||||
onClick={[Function]}
|
||||
size="s"
|
||||
|
@ -148,6 +155,7 @@ exports[`Transform: <GroupByLabelForm /> Histogram aggregation 1`] = `
|
|||
>
|
||||
<EuiButtonIcon
|
||||
aria-label="Delete item"
|
||||
data-test-subj="transformGroupByEntryDeleteButton"
|
||||
iconType="cross"
|
||||
onClick={[Function]}
|
||||
size="s"
|
||||
|
@ -167,6 +175,7 @@ exports[`Transform: <GroupByLabelForm /> Terms aggregation 1`] = `
|
|||
>
|
||||
<span
|
||||
className="eui-textTruncate"
|
||||
data-test-subj="transformGroupByEntryLabel"
|
||||
>
|
||||
the-group-by-agg-name
|
||||
</span>
|
||||
|
@ -180,6 +189,7 @@ exports[`Transform: <GroupByLabelForm /> Terms aggregation 1`] = `
|
|||
button={
|
||||
<EuiButtonIcon
|
||||
aria-label="Edit interval"
|
||||
data-test-subj="transformGroupByEntryEditButton"
|
||||
iconType="pencil"
|
||||
onClick={[Function]}
|
||||
size="s"
|
||||
|
@ -214,6 +224,7 @@ exports[`Transform: <GroupByLabelForm /> Terms aggregation 1`] = `
|
|||
>
|
||||
<EuiButtonIcon
|
||||
aria-label="Delete item"
|
||||
data-test-subj="transformGroupByEntryDeleteButton"
|
||||
iconType="cross"
|
||||
onClick={[Function]}
|
||||
size="s"
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
exports[`Transform: <GroupByListForm /> Minimal initialization 1`] = `
|
||||
<Fragment>
|
||||
<EuiPanel
|
||||
data-test-subj="transformGroupByEntry 0"
|
||||
paddingSize="s"
|
||||
>
|
||||
<GroupByLabelForm
|
||||
|
|
|
@ -53,14 +53,20 @@ export const GroupByLabelForm: React.SFC<Props> = ({
|
|||
return (
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false}>
|
||||
<EuiFlexItem className="transform__GroupByLabel--text">
|
||||
<span className="eui-textTruncate">{item.aggName}</span>
|
||||
<span className="eui-textTruncate" data-test-subj="transformGroupByEntryLabel">
|
||||
{item.aggName}
|
||||
</span>
|
||||
</EuiFlexItem>
|
||||
{interval !== undefined && (
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
className="transform__GroupByLabel--text transform__GroupByLabel--interval"
|
||||
>
|
||||
<EuiTextColor color="subdued" className="eui-textTruncate">
|
||||
<EuiTextColor
|
||||
color="subdued"
|
||||
className="eui-textTruncate"
|
||||
data-test-subj="transformGroupByEntryIntervalLabel"
|
||||
>
|
||||
{interval}
|
||||
</EuiTextColor>
|
||||
</EuiFlexItem>
|
||||
|
@ -77,6 +83,7 @@ export const GroupByLabelForm: React.SFC<Props> = ({
|
|||
size="s"
|
||||
iconType="pencil"
|
||||
onClick={() => setPopoverVisibility(!isPopoverVisible)}
|
||||
data-test-subj="transformGroupByEntryEditButton"
|
||||
/>
|
||||
}
|
||||
isOpen={isPopoverVisible}
|
||||
|
@ -98,6 +105,7 @@ export const GroupByLabelForm: React.SFC<Props> = ({
|
|||
size="s"
|
||||
iconType="cross"
|
||||
onClick={() => deleteHandler(item.aggName)}
|
||||
data-test-subj="transformGroupByEntryDeleteButton"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -33,11 +33,11 @@ export const GroupByListForm: React.SFC<ListProps> = ({
|
|||
const listKeys = Object.keys(list);
|
||||
return (
|
||||
<Fragment>
|
||||
{listKeys.map((aggName: AggName) => {
|
||||
{listKeys.map((aggName: AggName, i) => {
|
||||
const otherAggNames = listKeys.filter(k => k !== aggName);
|
||||
return (
|
||||
<Fragment key={aggName}>
|
||||
<EuiPanel paddingSize="s">
|
||||
<EuiPanel paddingSize="s" data-test-subj={`transformGroupByEntry ${i}`}>
|
||||
<GroupByLabelForm
|
||||
deleteHandler={deleteHandler}
|
||||
item={list[aggName]}
|
||||
|
|
|
@ -134,7 +134,7 @@ export const SourceIndexPreview: React.SFC<Props> = React.memo(({ cellClick, que
|
|||
|
||||
if (status === SOURCE_INDEX_STATUS.ERROR) {
|
||||
return (
|
||||
<EuiPanel grow={false}>
|
||||
<EuiPanel grow={false} data-test-subj="transformSourceIndexPreview error">
|
||||
<SourceIndexPreviewTitle indexPatternTitle={indexPattern.title} />
|
||||
<EuiCallOut
|
||||
title={i18n.translate('xpack.transform.sourceIndexPreview.sourceIndexPatternError', {
|
||||
|
@ -153,7 +153,7 @@ export const SourceIndexPreview: React.SFC<Props> = React.memo(({ cellClick, que
|
|||
|
||||
if (status === SOURCE_INDEX_STATUS.LOADED && tableItems.length === 0) {
|
||||
return (
|
||||
<EuiPanel grow={false}>
|
||||
<EuiPanel grow={false} data-test-subj="transformSourceIndexPreview empty">
|
||||
<SourceIndexPreviewTitle indexPatternTitle={indexPattern.title} />
|
||||
<EuiCallOut
|
||||
title={i18n.translate(
|
||||
|
@ -320,7 +320,7 @@ export const SourceIndexPreview: React.SFC<Props> = React.memo(({ cellClick, que
|
|||
});
|
||||
|
||||
return (
|
||||
<EuiPanel grow={false}>
|
||||
<EuiPanel grow={false} data-test-subj="transformSourceIndexPreview loaded">
|
||||
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<SourceIndexPreviewTitle indexPatternTitle={indexPattern.title} />
|
||||
|
|
|
@ -257,169 +257,197 @@ export const StepCreateForm: SFC<Props> = React.memo(
|
|||
}
|
||||
|
||||
return (
|
||||
<EuiForm>
|
||||
{!created && (
|
||||
<div data-test-subj="transformStepCreateForm">
|
||||
<EuiForm>
|
||||
{!created && (
|
||||
<EuiFlexGroup alignItems="center" style={FLEX_GROUP_STYLE}>
|
||||
<EuiFlexItem grow={false} style={FLEX_ITEM_STYLE}>
|
||||
<EuiButton
|
||||
fill
|
||||
isDisabled={created && started}
|
||||
onClick={createAndStartTransform}
|
||||
data-test-subj="transformWizardCreateAndStartButton"
|
||||
>
|
||||
{i18n.translate('xpack.transform.stepCreateForm.createAndStartTransformButton', {
|
||||
defaultMessage: 'Create and start',
|
||||
})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText color="subdued" size="s">
|
||||
{i18n.translate(
|
||||
'xpack.transform.stepCreateForm.createAndStartTransformDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'Creates and starts the transform. A transform will increase search and indexing load in your cluster. Please stop the transform if excessive load is experienced. After the transform is started, you will be offered options to continue exploring the transform.',
|
||||
}
|
||||
)}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
{created && (
|
||||
<EuiFlexGroup alignItems="center" style={FLEX_GROUP_STYLE}>
|
||||
<EuiFlexItem grow={false} style={FLEX_ITEM_STYLE}>
|
||||
<EuiButton
|
||||
fill
|
||||
isDisabled={created && started}
|
||||
onClick={startTransform}
|
||||
data-test-subj="transformWizardStartButton"
|
||||
>
|
||||
{i18n.translate('xpack.transform.stepCreateForm.startTransformButton', {
|
||||
defaultMessage: 'Start',
|
||||
})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText color="subdued" size="s">
|
||||
{i18n.translate('xpack.transform.stepCreateForm.startTransformDescription', {
|
||||
defaultMessage:
|
||||
'Starts the transform. A transform will increase search and indexing load in your cluster. Please stop the transform if excessive load is experienced. After the transform is started, you will be offered options to continue exploring the transform.',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
<EuiFlexGroup alignItems="center" style={FLEX_GROUP_STYLE}>
|
||||
<EuiFlexItem grow={false} style={FLEX_ITEM_STYLE}>
|
||||
<EuiButton fill isDisabled={created && started} onClick={createAndStartTransform}>
|
||||
{i18n.translate('xpack.transform.stepCreateForm.createAndStartTransformButton', {
|
||||
defaultMessage: 'Create and start',
|
||||
<EuiButton
|
||||
isDisabled={created}
|
||||
onClick={createTransform}
|
||||
data-test-subj="transformWizardCreateButton"
|
||||
>
|
||||
{i18n.translate('xpack.transform.stepCreateForm.createTransformButton', {
|
||||
defaultMessage: 'Create',
|
||||
})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText color="subdued" size="s">
|
||||
{i18n.translate('xpack.transform.stepCreateForm.createTransformDescription', {
|
||||
defaultMessage:
|
||||
'Create the transform without starting it. You will be able to start the transform later by returning to the transforms list.',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiFlexGroup alignItems="center" style={FLEX_GROUP_STYLE}>
|
||||
<EuiFlexItem grow={false} style={FLEX_ITEM_STYLE}>
|
||||
<EuiCopy textToCopy={getTransformConfigDevConsoleStatement()}>
|
||||
{(copy: () => void) => (
|
||||
<EuiButton
|
||||
onClick={copy}
|
||||
style={{ width: '100%' }}
|
||||
data-test-subj="transformWizardCopyToClipboardButton"
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.transform.stepCreateForm.copyTransformConfigToClipboardButton',
|
||||
{
|
||||
defaultMessage: 'Copy to clipboard',
|
||||
}
|
||||
)}
|
||||
</EuiButton>
|
||||
)}
|
||||
</EuiCopy>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText color="subdued" size="s">
|
||||
{i18n.translate(
|
||||
'xpack.transform.stepCreateForm.createAndStartTransformDescription',
|
||||
'xpack.transform.stepCreateForm.copyTransformConfigToClipboardDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'Creates and starts the transform. A transform will increase search and indexing load in your cluster. Please stop the transform if excessive load is experienced. After the transform is started, you will be offered options to continue exploring the transform.',
|
||||
'Copies to the clipboard the Kibana Dev Console command for creating the transform.',
|
||||
}
|
||||
)}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
{created && (
|
||||
<EuiFlexGroup alignItems="center" style={FLEX_GROUP_STYLE}>
|
||||
<EuiFlexItem grow={false} style={FLEX_ITEM_STYLE}>
|
||||
<EuiButton fill isDisabled={created && started} onClick={startTransform}>
|
||||
{i18n.translate('xpack.transform.stepCreateForm.startTransformButton', {
|
||||
defaultMessage: 'Start',
|
||||
})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText color="subdued" size="s">
|
||||
{i18n.translate('xpack.transform.stepCreateForm.startTransformDescription', {
|
||||
defaultMessage:
|
||||
'Starts the transform. A transform will increase search and indexing load in your cluster. Please stop the transform if excessive load is experienced. After the transform is started, you will be offered options to continue exploring the transform.',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
<EuiFlexGroup alignItems="center" style={FLEX_GROUP_STYLE}>
|
||||
<EuiFlexItem grow={false} style={FLEX_ITEM_STYLE}>
|
||||
<EuiButton isDisabled={created} onClick={createTransform}>
|
||||
{i18n.translate('xpack.transform.stepCreateForm.createTransformButton', {
|
||||
defaultMessage: 'Create',
|
||||
})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText color="subdued" size="s">
|
||||
{i18n.translate('xpack.transform.stepCreateForm.createTransformDescription', {
|
||||
defaultMessage:
|
||||
'Create the transform without starting it. You will be able to start the transform later by returning to the transforms list.',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiFlexGroup alignItems="center" style={FLEX_GROUP_STYLE}>
|
||||
<EuiFlexItem grow={false} style={FLEX_ITEM_STYLE}>
|
||||
<EuiCopy textToCopy={getTransformConfigDevConsoleStatement()}>
|
||||
{(copy: () => void) => (
|
||||
<EuiButton onClick={copy} style={{ width: '100%' }}>
|
||||
{i18n.translate(
|
||||
'xpack.transform.stepCreateForm.copyTransformConfigToClipboardButton',
|
||||
{
|
||||
defaultMessage: 'Copy to clipboard',
|
||||
}
|
||||
)}
|
||||
</EuiButton>
|
||||
)}
|
||||
</EuiCopy>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText color="subdued" size="s">
|
||||
{i18n.translate(
|
||||
'xpack.transform.stepCreateForm.copyTransformConfigToClipboardDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'Copies to the clipboard the Kibana Dev Console command for creating the transform.',
|
||||
}
|
||||
)}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
{progressPercentComplete !== undefined && isBatchTransform && (
|
||||
<Fragment>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiText size="xs">
|
||||
<strong>
|
||||
{i18n.translate('xpack.transform.stepCreateForm.progressTitle', {
|
||||
defaultMessage: 'Progress',
|
||||
})}
|
||||
</strong>
|
||||
</EuiText>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem style={{ width: '400px' }} grow={false}>
|
||||
<EuiProgress size="l" color="primary" value={progressPercentComplete} max={100} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText size="xs">{progressPercentComplete}%</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</Fragment>
|
||||
)}
|
||||
{created && (
|
||||
<Fragment>
|
||||
<EuiHorizontalRule />
|
||||
<EuiFlexGrid gutterSize="l">
|
||||
<EuiFlexItem style={PANEL_ITEM_STYLE}>
|
||||
<EuiCard
|
||||
icon={<EuiIcon size="xxl" type="list" />}
|
||||
title={i18n.translate('xpack.transform.stepCreateForm.transformListCardTitle', {
|
||||
defaultMessage: 'Transforms',
|
||||
{progressPercentComplete !== undefined && isBatchTransform && (
|
||||
<Fragment>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiText size="xs">
|
||||
<strong>
|
||||
{i18n.translate('xpack.transform.stepCreateForm.progressTitle', {
|
||||
defaultMessage: 'Progress',
|
||||
})}
|
||||
description={i18n.translate(
|
||||
'xpack.transform.stepCreateForm.transformListCardDescription',
|
||||
{
|
||||
defaultMessage: 'Return to the transform management page.',
|
||||
}
|
||||
)}
|
||||
onClick={() => setRedirectToTransformManagement(true)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
{started === true && createIndexPattern === true && indexPatternId === undefined && (
|
||||
<EuiFlexItem style={PANEL_ITEM_STYLE}>
|
||||
<EuiPanel style={{ position: 'relative' }}>
|
||||
<EuiProgress size="xs" color="primary" position="absolute" />
|
||||
<EuiText color="subdued" size="s">
|
||||
<p>
|
||||
{i18n.translate(
|
||||
'xpack.transform.stepCreateForm.creatingIndexPatternMessage',
|
||||
{
|
||||
defaultMessage: 'Creating Kibana index pattern ...',
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{started === true && indexPatternId !== undefined && (
|
||||
<EuiFlexItem style={PANEL_ITEM_STYLE}>
|
||||
<EuiCard
|
||||
icon={<EuiIcon size="xxl" type="discoverApp" />}
|
||||
title={i18n.translate('xpack.transform.stepCreateForm.discoverCardTitle', {
|
||||
defaultMessage: 'Discover',
|
||||
})}
|
||||
description={i18n.translate(
|
||||
'xpack.transform.stepCreateForm.discoverCardDescription',
|
||||
{
|
||||
defaultMessage: 'Use Discover to explore the transform.',
|
||||
}
|
||||
)}
|
||||
href={getDiscoverUrl(indexPatternId, kibanaContext.kbnBaseUrl)}
|
||||
</strong>
|
||||
</EuiText>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem style={{ width: '400px' }} grow={false}>
|
||||
<EuiProgress
|
||||
size="l"
|
||||
color="primary"
|
||||
value={progressPercentComplete}
|
||||
max={100}
|
||||
data-test-subj="transformWizardProgressBar"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGrid>
|
||||
</Fragment>
|
||||
)}
|
||||
</EuiForm>
|
||||
<EuiFlexItem>
|
||||
<EuiText size="xs">{progressPercentComplete}%</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</Fragment>
|
||||
)}
|
||||
{created && (
|
||||
<Fragment>
|
||||
<EuiHorizontalRule />
|
||||
<EuiFlexGrid gutterSize="l">
|
||||
<EuiFlexItem style={PANEL_ITEM_STYLE}>
|
||||
<EuiCard
|
||||
icon={<EuiIcon size="xxl" type="list" />}
|
||||
title={i18n.translate('xpack.transform.stepCreateForm.transformListCardTitle', {
|
||||
defaultMessage: 'Transforms',
|
||||
})}
|
||||
description={i18n.translate(
|
||||
'xpack.transform.stepCreateForm.transformListCardDescription',
|
||||
{
|
||||
defaultMessage: 'Return to the transform management page.',
|
||||
}
|
||||
)}
|
||||
onClick={() => setRedirectToTransformManagement(true)}
|
||||
data-test-subj="transformWizardCardManagement"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
{started === true && createIndexPattern === true && indexPatternId === undefined && (
|
||||
<EuiFlexItem style={PANEL_ITEM_STYLE}>
|
||||
<EuiPanel style={{ position: 'relative' }}>
|
||||
<EuiProgress size="xs" color="primary" position="absolute" />
|
||||
<EuiText color="subdued" size="s">
|
||||
<p>
|
||||
{i18n.translate(
|
||||
'xpack.transform.stepCreateForm.creatingIndexPatternMessage',
|
||||
{
|
||||
defaultMessage: 'Creating Kibana index pattern ...',
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{started === true && indexPatternId !== undefined && (
|
||||
<EuiFlexItem style={PANEL_ITEM_STYLE}>
|
||||
<EuiCard
|
||||
icon={<EuiIcon size="xxl" type="discoverApp" />}
|
||||
title={i18n.translate('xpack.transform.stepCreateForm.discoverCardTitle', {
|
||||
defaultMessage: 'Discover',
|
||||
})}
|
||||
description={i18n.translate(
|
||||
'xpack.transform.stepCreateForm.discoverCardDescription',
|
||||
{
|
||||
defaultMessage: 'Use Discover to explore the transform.',
|
||||
}
|
||||
)}
|
||||
href={getDiscoverUrl(indexPatternId, kibanaContext.kbnBaseUrl)}
|
||||
data-test-subj="transformWizardCardDiscover"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGrid>
|
||||
</Fragment>
|
||||
)}
|
||||
</EuiForm>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -158,7 +158,7 @@ export const PivotPreview: SFC<PivotPreviewProps> = React.memo(({ aggs, groupBy,
|
|||
|
||||
if (status === PIVOT_PREVIEW_STATUS.ERROR) {
|
||||
return (
|
||||
<EuiPanel grow={false}>
|
||||
<EuiPanel grow={false} data-test-subj="transformPivotPreview error">
|
||||
<PreviewTitle previewRequest={previewRequest} />
|
||||
<EuiCallOut
|
||||
title={i18n.translate('xpack.transform.pivotPreview.PivotPreviewError', {
|
||||
|
@ -192,7 +192,7 @@ export const PivotPreview: SFC<PivotPreviewProps> = React.memo(({ aggs, groupBy,
|
|||
);
|
||||
}
|
||||
return (
|
||||
<EuiPanel grow={false}>
|
||||
<EuiPanel grow={false} data-test-subj="transformPivotPreview empty">
|
||||
<PreviewTitle previewRequest={previewRequest} />
|
||||
<EuiCallOut
|
||||
title={i18n.translate('xpack.transform.pivotPreview.PivotPreviewNoDataCalloutTitle', {
|
||||
|
@ -257,7 +257,7 @@ export const PivotPreview: SFC<PivotPreviewProps> = React.memo(({ aggs, groupBy,
|
|||
};
|
||||
|
||||
return (
|
||||
<EuiPanel>
|
||||
<EuiPanel data-test-subj="transformPivotPreview loaded">
|
||||
<PreviewTitle previewRequest={previewRequest} />
|
||||
{status === PIVOT_PREVIEW_STATUS.LOADING && <EuiProgress size="xs" color="accent" />}
|
||||
{status !== PIVOT_PREVIEW_STATUS.LOADING && (
|
||||
|
|
|
@ -505,145 +505,318 @@ export const StepDefineForm: SFC<Props> = React.memo(({ overrides = {}, onChange
|
|||
return (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false} style={{ minWidth: '420px' }}>
|
||||
<EuiForm>
|
||||
{kibanaContext.currentSavedSearch === undefined && typeof searchString === 'string' && (
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineForm.indexPatternLabel', {
|
||||
defaultMessage: 'Index pattern',
|
||||
})}
|
||||
helpText={
|
||||
disabledQuery
|
||||
? i18n.translate('xpack.transform.stepDefineForm.indexPatternHelpText', {
|
||||
defaultMessage:
|
||||
'An optional query for this index pattern is not supported. The number of supported index fields is {maxIndexFields} whereas this index has {numIndexFields} fields.',
|
||||
values: {
|
||||
maxIndexFields,
|
||||
numIndexFields,
|
||||
},
|
||||
})
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<span>{indexPattern.title}</span>
|
||||
</EuiFormRow>
|
||||
{!disabledQuery && (
|
||||
<Fragment>
|
||||
{!isAdvancedSourceEditorEnabled && (
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineForm.queryLabel', {
|
||||
defaultMessage: 'Query',
|
||||
})}
|
||||
helpText={i18n.translate('xpack.transform.stepDefineForm.queryHelpText', {
|
||||
defaultMessage: 'Use a query to filter the source data (optional).',
|
||||
})}
|
||||
>
|
||||
<KqlFilterBar
|
||||
indexPattern={indexPattern}
|
||||
onSubmit={searchHandler}
|
||||
initialValue={searchString === defaultSearch ? emptySearch : searchString}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.queryPlaceholder',
|
||||
{
|
||||
defaultMessage: 'e.g. {example}',
|
||||
values: { example: 'method : "GET" or status : "404"' },
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{isAdvancedSourceEditorEnabled && (
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineForm.advancedSourceEditorLabel', {
|
||||
defaultMessage: 'Source query clause',
|
||||
})}
|
||||
helpText={advancedSourceEditorHelpText}
|
||||
>
|
||||
<EuiPanel grow={false} paddingSize="none">
|
||||
<EuiCodeEditor
|
||||
mode="json"
|
||||
width="100%"
|
||||
value={advancedEditorSourceConfig}
|
||||
onChange={(d: string) => {
|
||||
setAdvancedEditorSourceConfig(d);
|
||||
|
||||
// Disable the "Apply"-Button if the config hasn't changed.
|
||||
if (advancedEditorSourceConfigLastApplied === d) {
|
||||
setAdvancedSourceEditorApplyButtonEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to parse the string passed on from the editor.
|
||||
// If parsing fails, the "Apply"-Button will be disabled
|
||||
try {
|
||||
JSON.parse(d);
|
||||
setAdvancedSourceEditorApplyButtonEnabled(true);
|
||||
} catch (e) {
|
||||
setAdvancedSourceEditorApplyButtonEnabled(false);
|
||||
}
|
||||
}}
|
||||
setOptions={{
|
||||
fontSize: '12px',
|
||||
}}
|
||||
theme="textmate"
|
||||
aria-label={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.advancedSourceEditorAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Advanced query editor',
|
||||
}
|
||||
<div data-test-subj="transformStepDefineForm">
|
||||
<EuiForm>
|
||||
{kibanaContext.currentSavedSearch === undefined && typeof searchString === 'string' && (
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineForm.indexPatternLabel', {
|
||||
defaultMessage: 'Index pattern',
|
||||
})}
|
||||
helpText={
|
||||
disabledQuery
|
||||
? i18n.translate('xpack.transform.stepDefineForm.indexPatternHelpText', {
|
||||
defaultMessage:
|
||||
'An optional query for this index pattern is not supported. The number of supported index fields is {maxIndexFields} whereas this index has {numIndexFields} fields.',
|
||||
values: {
|
||||
maxIndexFields,
|
||||
numIndexFields,
|
||||
},
|
||||
})
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<span>{indexPattern.title}</span>
|
||||
</EuiFormRow>
|
||||
{!disabledQuery && (
|
||||
<Fragment>
|
||||
{!isAdvancedSourceEditorEnabled && (
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineForm.queryLabel', {
|
||||
defaultMessage: 'Query',
|
||||
})}
|
||||
helpText={i18n.translate('xpack.transform.stepDefineForm.queryHelpText', {
|
||||
defaultMessage: 'Use a query to filter the source data (optional).',
|
||||
})}
|
||||
>
|
||||
<KqlFilterBar
|
||||
indexPattern={indexPattern}
|
||||
onSubmit={searchHandler}
|
||||
initialValue={searchString === defaultSearch ? emptySearch : searchString}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.queryPlaceholder',
|
||||
{
|
||||
defaultMessage: 'e.g. {example}',
|
||||
values: { example: 'method : "GET" or status : "404"' },
|
||||
}
|
||||
)}
|
||||
testSubj="tarnsformQueryInput"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{isAdvancedSourceEditorEnabled && (
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
label={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.advancedSourceEditorLabel',
|
||||
{
|
||||
defaultMessage: 'Source query clause',
|
||||
}
|
||||
)}
|
||||
helpText={advancedSourceEditorHelpText}
|
||||
>
|
||||
<EuiPanel grow={false} paddingSize="none">
|
||||
<EuiCodeEditor
|
||||
mode="json"
|
||||
width="100%"
|
||||
value={advancedEditorSourceConfig}
|
||||
onChange={(d: string) => {
|
||||
setAdvancedEditorSourceConfig(d);
|
||||
|
||||
// Disable the "Apply"-Button if the config hasn't changed.
|
||||
if (advancedEditorSourceConfigLastApplied === d) {
|
||||
setAdvancedSourceEditorApplyButtonEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to parse the string passed on from the editor.
|
||||
// If parsing fails, the "Apply"-Button will be disabled
|
||||
try {
|
||||
JSON.parse(d);
|
||||
setAdvancedSourceEditorApplyButtonEnabled(true);
|
||||
} catch (e) {
|
||||
setAdvancedSourceEditorApplyButtonEnabled(false);
|
||||
}
|
||||
}}
|
||||
setOptions={{
|
||||
fontSize: '12px',
|
||||
}}
|
||||
theme="textmate"
|
||||
aria-label={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.advancedSourceEditorAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Advanced query editor',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFormRow>
|
||||
</Fragment>
|
||||
)}
|
||||
{kibanaContext.currentSavedSearch === undefined && (
|
||||
<EuiFormRow>
|
||||
<EuiFlexGroup gutterSize="none">
|
||||
<EuiFlexItem>
|
||||
<EuiSwitch
|
||||
label={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.advancedEditorSourceConfigSwitchLabel',
|
||||
{
|
||||
defaultMessage: 'Advanced query editor',
|
||||
}
|
||||
)}
|
||||
checked={isAdvancedSourceEditorEnabled}
|
||||
onChange={() => {
|
||||
if (isAdvancedSourceEditorEnabled && sourceConfigUpdated) {
|
||||
setAdvancedSourceEditorSwitchModalVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
toggleAdvancedSourceEditor();
|
||||
}}
|
||||
data-test-subj="transformAdvancedQueryEditorSwitch"
|
||||
/>
|
||||
{isAdvancedSourceEditorSwitchModalVisible && (
|
||||
<SwitchModal
|
||||
onCancel={() => setAdvancedSourceEditorSwitchModalVisible(false)}
|
||||
onConfirm={() => {
|
||||
setAdvancedSourceEditorSwitchModalVisible(false);
|
||||
toggleAdvancedSourceEditor(true);
|
||||
}}
|
||||
type={'source'}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
{isAdvancedSourceEditorEnabled && (
|
||||
<EuiButton
|
||||
size="s"
|
||||
fill
|
||||
onClick={applyAdvancedSourceEditorChanges}
|
||||
disabled={!isAdvancedSourceEditorApplyButtonEnabled}
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.transform.stepDefineForm.advancedSourceEditorApplyButtonText',
|
||||
{
|
||||
defaultMessage: 'Apply changes',
|
||||
}
|
||||
)}
|
||||
</EuiButton>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
</EuiFormRow>
|
||||
</Fragment>
|
||||
)}
|
||||
{kibanaContext.currentSavedSearch === undefined && (
|
||||
)}
|
||||
{kibanaContext.currentSavedSearch !== undefined &&
|
||||
kibanaContext.currentSavedSearch.id !== undefined && (
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineForm.savedSearchLabel', {
|
||||
defaultMessage: 'Saved search',
|
||||
})}
|
||||
>
|
||||
<span>{kibanaContext.currentSavedSearch.title}</span>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
|
||||
{!isAdvancedPivotEditorEnabled && (
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineForm.groupByLabel', {
|
||||
defaultMessage: 'Group by',
|
||||
})}
|
||||
>
|
||||
<Fragment>
|
||||
<GroupByListForm
|
||||
list={groupByList}
|
||||
options={groupByOptionsData}
|
||||
onChange={updateGroupBy}
|
||||
deleteHandler={deleteGroupBy}
|
||||
/>
|
||||
<DropDown
|
||||
changeHandler={addGroupBy}
|
||||
options={groupByOptions}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.groupByPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Add a group by field ...',
|
||||
}
|
||||
)}
|
||||
testSubj="transformGroupBySelection"
|
||||
/>
|
||||
</Fragment>
|
||||
</EuiFormRow>
|
||||
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineForm.aggregationsLabel', {
|
||||
defaultMessage: 'Aggregations',
|
||||
})}
|
||||
>
|
||||
<Fragment>
|
||||
<AggListForm
|
||||
list={aggList}
|
||||
options={aggOptionsData}
|
||||
onChange={updateAggregation}
|
||||
deleteHandler={deleteAggregation}
|
||||
/>
|
||||
<DropDown
|
||||
changeHandler={addAggregation}
|
||||
options={aggOptions}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.aggregationsPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Add an aggregation ...',
|
||||
}
|
||||
)}
|
||||
testSubj="transformAggregationSelection"
|
||||
/>
|
||||
</Fragment>
|
||||
</EuiFormRow>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{isAdvancedPivotEditorEnabled && (
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineForm.advancedEditorLabel', {
|
||||
defaultMessage: 'Pivot configuration object',
|
||||
})}
|
||||
helpText={advancedEditorHelpText}
|
||||
>
|
||||
<EuiPanel grow={false} paddingSize="none">
|
||||
<EuiCodeEditor
|
||||
mode="json"
|
||||
width="100%"
|
||||
value={advancedEditorConfig}
|
||||
onChange={(d: string) => {
|
||||
setAdvancedEditorConfig(d);
|
||||
|
||||
// Disable the "Apply"-Button if the config hasn't changed.
|
||||
if (advancedEditorConfigLastApplied === d) {
|
||||
setAdvancedPivotEditorApplyButtonEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to parse the string passed on from the editor.
|
||||
// If parsing fails, the "Apply"-Button will be disabled
|
||||
try {
|
||||
JSON.parse(d);
|
||||
setAdvancedPivotEditorApplyButtonEnabled(true);
|
||||
} catch (e) {
|
||||
setAdvancedPivotEditorApplyButtonEnabled(false);
|
||||
}
|
||||
}}
|
||||
setOptions={{
|
||||
fontSize: '12px',
|
||||
}}
|
||||
theme="textmate"
|
||||
aria-label={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.advancedEditorAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Advanced pivot editor',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFormRow>
|
||||
</Fragment>
|
||||
)}
|
||||
<EuiFormRow>
|
||||
<EuiFlexGroup gutterSize="none">
|
||||
<EuiFlexItem>
|
||||
<EuiSwitch
|
||||
label={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.advancedEditorSourceConfigSwitchLabel',
|
||||
'xpack.transform.stepDefineForm.advancedEditorSwitchLabel',
|
||||
{
|
||||
defaultMessage: 'Advanced query editor',
|
||||
defaultMessage: 'Advanced pivot editor',
|
||||
}
|
||||
)}
|
||||
checked={isAdvancedSourceEditorEnabled}
|
||||
checked={isAdvancedPivotEditorEnabled}
|
||||
onChange={() => {
|
||||
if (isAdvancedSourceEditorEnabled && sourceConfigUpdated) {
|
||||
setAdvancedSourceEditorSwitchModalVisible(true);
|
||||
if (
|
||||
isAdvancedPivotEditorEnabled &&
|
||||
(isAdvancedPivotEditorApplyButtonEnabled ||
|
||||
advancedEditorConfig !== advancedEditorConfigLastApplied)
|
||||
) {
|
||||
setAdvancedEditorSwitchModalVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
toggleAdvancedSourceEditor();
|
||||
toggleAdvancedEditor();
|
||||
}}
|
||||
data-test-subj="transformAdvancedPivotEditorSwitch"
|
||||
/>
|
||||
{isAdvancedSourceEditorSwitchModalVisible && (
|
||||
{isAdvancedEditorSwitchModalVisible && (
|
||||
<SwitchModal
|
||||
onCancel={() => setAdvancedSourceEditorSwitchModalVisible(false)}
|
||||
onCancel={() => setAdvancedEditorSwitchModalVisible(false)}
|
||||
onConfirm={() => {
|
||||
setAdvancedSourceEditorSwitchModalVisible(false);
|
||||
toggleAdvancedSourceEditor(true);
|
||||
setAdvancedEditorSwitchModalVisible(false);
|
||||
toggleAdvancedEditor();
|
||||
}}
|
||||
type={'source'}
|
||||
type={'pivot'}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
{isAdvancedSourceEditorEnabled && (
|
||||
{isAdvancedPivotEditorEnabled && (
|
||||
<EuiButton
|
||||
size="s"
|
||||
fill
|
||||
onClick={applyAdvancedSourceEditorChanges}
|
||||
disabled={!isAdvancedSourceEditorApplyButtonEnabled}
|
||||
onClick={applyAdvancedPivotEditorChanges}
|
||||
disabled={!isAdvancedPivotEditorApplyButtonEnabled}
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.transform.stepDefineForm.advancedSourceEditorApplyButtonText',
|
||||
'xpack.transform.stepDefineForm.advancedEditorApplyButtonText',
|
||||
{
|
||||
defaultMessage: 'Apply changes',
|
||||
}
|
||||
|
@ -652,179 +825,19 @@ export const StepDefineForm: SFC<Props> = React.memo(({ overrides = {}, onChange
|
|||
)}
|
||||
</EuiFlexGroup>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
{kibanaContext.currentSavedSearch !== undefined &&
|
||||
kibanaContext.currentSavedSearch.id !== undefined && (
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineForm.savedSearchLabel', {
|
||||
defaultMessage: 'Saved search',
|
||||
})}
|
||||
>
|
||||
<span>{kibanaContext.currentSavedSearch.title}</span>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
|
||||
{!isAdvancedPivotEditorEnabled && (
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineForm.groupByLabel', {
|
||||
defaultMessage: 'Group by',
|
||||
})}
|
||||
>
|
||||
<Fragment>
|
||||
<GroupByListForm
|
||||
list={groupByList}
|
||||
options={groupByOptionsData}
|
||||
onChange={updateGroupBy}
|
||||
deleteHandler={deleteGroupBy}
|
||||
/>
|
||||
<DropDown
|
||||
changeHandler={addGroupBy}
|
||||
options={groupByOptions}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.groupByPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Add a group by field ...',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</Fragment>
|
||||
</EuiFormRow>
|
||||
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineForm.aggregationsLabel', {
|
||||
defaultMessage: 'Aggregations',
|
||||
})}
|
||||
>
|
||||
<Fragment>
|
||||
<AggListForm
|
||||
list={aggList}
|
||||
options={aggOptionsData}
|
||||
onChange={updateAggregation}
|
||||
deleteHandler={deleteAggregation}
|
||||
/>
|
||||
<DropDown
|
||||
changeHandler={addAggregation}
|
||||
options={aggOptions}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.aggregationsPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Add an aggregation ...',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</Fragment>
|
||||
</EuiFormRow>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{isAdvancedPivotEditorEnabled && (
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineForm.advancedEditorLabel', {
|
||||
defaultMessage: 'Pivot configuration object',
|
||||
})}
|
||||
helpText={advancedEditorHelpText}
|
||||
>
|
||||
<EuiPanel grow={false} paddingSize="none">
|
||||
<EuiCodeEditor
|
||||
mode="json"
|
||||
width="100%"
|
||||
value={advancedEditorConfig}
|
||||
onChange={(d: string) => {
|
||||
setAdvancedEditorConfig(d);
|
||||
|
||||
// Disable the "Apply"-Button if the config hasn't changed.
|
||||
if (advancedEditorConfigLastApplied === d) {
|
||||
setAdvancedPivotEditorApplyButtonEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to parse the string passed on from the editor.
|
||||
// If parsing fails, the "Apply"-Button will be disabled
|
||||
try {
|
||||
JSON.parse(d);
|
||||
setAdvancedPivotEditorApplyButtonEnabled(true);
|
||||
} catch (e) {
|
||||
setAdvancedPivotEditorApplyButtonEnabled(false);
|
||||
}
|
||||
}}
|
||||
setOptions={{
|
||||
fontSize: '12px',
|
||||
}}
|
||||
theme="textmate"
|
||||
aria-label={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.advancedEditorAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Advanced pivot editor',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFormRow>
|
||||
</Fragment>
|
||||
)}
|
||||
<EuiFormRow>
|
||||
<EuiFlexGroup gutterSize="none">
|
||||
<EuiFlexItem>
|
||||
<EuiSwitch
|
||||
label={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.advancedEditorSwitchLabel',
|
||||
{
|
||||
defaultMessage: 'Advanced pivot editor',
|
||||
}
|
||||
)}
|
||||
checked={isAdvancedPivotEditorEnabled}
|
||||
onChange={() => {
|
||||
if (
|
||||
isAdvancedPivotEditorEnabled &&
|
||||
(isAdvancedPivotEditorApplyButtonEnabled ||
|
||||
advancedEditorConfig !== advancedEditorConfigLastApplied)
|
||||
) {
|
||||
setAdvancedEditorSwitchModalVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
toggleAdvancedEditor();
|
||||
}}
|
||||
/>
|
||||
{isAdvancedEditorSwitchModalVisible && (
|
||||
<SwitchModal
|
||||
onCancel={() => setAdvancedEditorSwitchModalVisible(false)}
|
||||
onConfirm={() => {
|
||||
setAdvancedEditorSwitchModalVisible(false);
|
||||
toggleAdvancedEditor();
|
||||
}}
|
||||
type={'pivot'}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
{isAdvancedPivotEditorEnabled && (
|
||||
<EuiButton
|
||||
size="s"
|
||||
fill
|
||||
onClick={applyAdvancedPivotEditorChanges}
|
||||
disabled={!isAdvancedPivotEditorApplyButtonEnabled}
|
||||
>
|
||||
{i18n.translate('xpack.transform.stepDefineForm.advancedEditorApplyButtonText', {
|
||||
defaultMessage: 'Apply changes',
|
||||
{!valid && (
|
||||
<Fragment>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFormHelpText style={{ maxWidth: '320px' }}>
|
||||
{i18n.translate('xpack.transform.stepDefineForm.formHelp', {
|
||||
defaultMessage:
|
||||
'Transforms are scalable and automated processes for pivoting. Choose at least one group-by and aggregation to get started.',
|
||||
})}
|
||||
</EuiButton>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
</EuiFormRow>
|
||||
{!valid && (
|
||||
<Fragment>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFormHelpText style={{ maxWidth: '320px' }}>
|
||||
{i18n.translate('xpack.transform.stepDefineForm.formHelp', {
|
||||
defaultMessage:
|
||||
'Transforms are scalable and automated processes for pivoting. Choose at least one group-by and aggregation to get started.',
|
||||
})}
|
||||
</EuiFormHelpText>
|
||||
</Fragment>
|
||||
)}
|
||||
</EuiForm>
|
||||
</EuiFormHelpText>
|
||||
</Fragment>
|
||||
)}
|
||||
</EuiForm>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem>
|
||||
|
|
|
@ -57,75 +57,80 @@ export const StepDefineSummary: FC<StepDefineExposedState> = ({
|
|||
return (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false} style={{ minWidth: '420px' }}>
|
||||
<EuiForm>
|
||||
{kibanaContext.currentSavedSearch !== undefined &&
|
||||
kibanaContext.currentSavedSearch.id === undefined &&
|
||||
typeof searchString === 'string' && (
|
||||
<Fragment>
|
||||
<div data-test-subj="transformStepDefineSummary">
|
||||
<EuiForm>
|
||||
{kibanaContext.currentSavedSearch !== undefined &&
|
||||
kibanaContext.currentSavedSearch.id === undefined &&
|
||||
typeof searchString === 'string' && (
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineSummary.indexPatternLabel', {
|
||||
defaultMessage: 'Index pattern',
|
||||
})}
|
||||
>
|
||||
<span>{kibanaContext.currentIndexPattern.title}</span>
|
||||
</EuiFormRow>
|
||||
{useCodeBlock === false && displaySearch !== emptySearch && (
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineSummary.queryLabel', {
|
||||
defaultMessage: 'Query',
|
||||
})}
|
||||
>
|
||||
<span>{displaySearch}</span>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
{useCodeBlock === true && displaySearch !== emptySearch && (
|
||||
<EuiFormRow
|
||||
label={i18n.translate(
|
||||
'xpack.transform.stepDefineSummary.queryCodeBlockLabel',
|
||||
{
|
||||
defaultMessage: 'Query',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<EuiCodeBlock
|
||||
language="js"
|
||||
fontSize="s"
|
||||
paddingSize="s"
|
||||
color="light"
|
||||
overflowHeight={300}
|
||||
isCopyable
|
||||
>
|
||||
{displaySearch}
|
||||
</EuiCodeBlock>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{kibanaContext.currentSavedSearch !== undefined &&
|
||||
kibanaContext.currentSavedSearch.id !== undefined && (
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineSummary.indexPatternLabel', {
|
||||
defaultMessage: 'Index pattern',
|
||||
label={i18n.translate('xpack.transform.stepDefineSummary.savedSearchLabel', {
|
||||
defaultMessage: 'Saved search',
|
||||
})}
|
||||
>
|
||||
<span>{kibanaContext.currentIndexPattern.title}</span>
|
||||
<span>{kibanaContext.currentSavedSearch.title}</span>
|
||||
</EuiFormRow>
|
||||
{useCodeBlock === false && displaySearch !== emptySearch && (
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineSummary.queryLabel', {
|
||||
defaultMessage: 'Query',
|
||||
})}
|
||||
>
|
||||
<span>{displaySearch}</span>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
{useCodeBlock === true && displaySearch !== emptySearch && (
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineSummary.queryCodeBlockLabel', {
|
||||
defaultMessage: 'Query',
|
||||
})}
|
||||
>
|
||||
<EuiCodeBlock
|
||||
language="js"
|
||||
fontSize="s"
|
||||
paddingSize="s"
|
||||
color="light"
|
||||
overflowHeight={300}
|
||||
isCopyable
|
||||
>
|
||||
{displaySearch}
|
||||
</EuiCodeBlock>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
)}
|
||||
|
||||
{kibanaContext.currentSavedSearch !== undefined &&
|
||||
kibanaContext.currentSavedSearch.id !== undefined && (
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineSummary.savedSearchLabel', {
|
||||
defaultMessage: 'Saved search',
|
||||
})}
|
||||
>
|
||||
<span>{kibanaContext.currentSavedSearch.title}</span>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineSummary.groupByLabel', {
|
||||
defaultMessage: 'Group by',
|
||||
})}
|
||||
>
|
||||
<GroupByListSummary list={groupByList} />
|
||||
</EuiFormRow>
|
||||
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineSummary.groupByLabel', {
|
||||
defaultMessage: 'Group by',
|
||||
})}
|
||||
>
|
||||
<GroupByListSummary list={groupByList} />
|
||||
</EuiFormRow>
|
||||
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineSummary.aggregationsLabel', {
|
||||
defaultMessage: 'Aggregations',
|
||||
})}
|
||||
>
|
||||
<AggListSummary list={aggList} />
|
||||
</EuiFormRow>
|
||||
</EuiForm>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDefineSummary.aggregationsLabel', {
|
||||
defaultMessage: 'Aggregations',
|
||||
})}
|
||||
>
|
||||
<AggListSummary list={aggList} />
|
||||
</EuiFormRow>
|
||||
</EuiForm>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem>
|
||||
|
|
|
@ -181,200 +181,217 @@ export const StepDetailsForm: SFC<Props> = React.memo(({ overrides = {}, onChang
|
|||
]);
|
||||
|
||||
return (
|
||||
<EuiForm>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDetailsForm.transformIdLabel', {
|
||||
defaultMessage: 'Transform ID',
|
||||
})}
|
||||
isInvalid={(!transformIdEmpty && !transformIdValid) || transformIdExists}
|
||||
error={[
|
||||
...(!transformIdEmpty && !transformIdValid
|
||||
? [
|
||||
i18n.translate('xpack.transform.stepDetailsForm.transformIdInvalidError', {
|
||||
defaultMessage:
|
||||
'Must contain lowercase alphanumeric characters (a-z and 0-9), hyphens, and underscores only and must start and end with alphanumeric characters.',
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
...(transformIdExists
|
||||
? [
|
||||
i18n.translate('xpack.transform.stepDetailsForm.transformIdExistsError', {
|
||||
defaultMessage: 'A transform with this ID already exists.',
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
]}
|
||||
>
|
||||
<EuiFieldText
|
||||
placeholder="transform ID"
|
||||
value={transformId}
|
||||
onChange={e => setTransformId(e.target.value)}
|
||||
aria-label={i18n.translate('xpack.transform.stepDetailsForm.transformIdInputAriaLabel', {
|
||||
defaultMessage: 'Choose a unique transform ID.',
|
||||
<div data-test-subj="transformStepDetailsForm">
|
||||
<EuiForm>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDetailsForm.transformIdLabel', {
|
||||
defaultMessage: 'Transform ID',
|
||||
})}
|
||||
isInvalid={(!transformIdEmpty && !transformIdValid) || transformIdExists}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDetailsForm.transformDescriptionLabel', {
|
||||
defaultMessage: 'Transform description',
|
||||
})}
|
||||
helpText={i18n.translate('xpack.transform.stepDetailsForm.transformDescriptionHelpText', {
|
||||
defaultMessage: 'Optional descriptive text.',
|
||||
})}
|
||||
>
|
||||
<EuiFieldText
|
||||
placeholder="transform description"
|
||||
value={transformDescription}
|
||||
onChange={e => setTransformDescription(e.target.value)}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.transformDescriptionInputAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Choose an optional transform description.',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDetailsForm.destinationIndexLabel', {
|
||||
defaultMessage: 'Destination index',
|
||||
})}
|
||||
isInvalid={!indexNameEmpty && !indexNameValid}
|
||||
helpText={
|
||||
indexNameExists &&
|
||||
i18n.translate('xpack.transform.stepDetailsForm.destinationIndexHelpText', {
|
||||
defaultMessage:
|
||||
'An index with this name already exists. Be aware that running this transform will modify this destination index.',
|
||||
})
|
||||
}
|
||||
error={
|
||||
!indexNameEmpty &&
|
||||
!indexNameValid && [
|
||||
<Fragment>
|
||||
{i18n.translate('xpack.transform.stepDetailsForm.destinationIndexInvalidError', {
|
||||
defaultMessage: 'Invalid destination index name.',
|
||||
})}
|
||||
<br />
|
||||
<EuiLink
|
||||
href={`https://www.elastic.co/guide/en/elasticsearch/reference/${metadata.branch}/indices-create-index.html#indices-create-index`}
|
||||
target="_blank"
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.destinationIndexInvalidErrorLink',
|
||||
{
|
||||
defaultMessage: 'Learn more about index name limitations.',
|
||||
}
|
||||
)}
|
||||
</EuiLink>
|
||||
</Fragment>,
|
||||
]
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
placeholder="destination index"
|
||||
value={destinationIndex}
|
||||
onChange={e => setDestinationIndex(e.target.value)}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.destinationIndexInputAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Choose a unique destination index name.',
|
||||
}
|
||||
)}
|
||||
error={[
|
||||
...(!transformIdEmpty && !transformIdValid
|
||||
? [
|
||||
i18n.translate('xpack.transform.stepDetailsForm.transformIdInvalidError', {
|
||||
defaultMessage:
|
||||
'Must contain lowercase alphanumeric characters (a-z and 0-9), hyphens, and underscores only and must start and end with alphanumeric characters.',
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
...(transformIdExists
|
||||
? [
|
||||
i18n.translate('xpack.transform.stepDetailsForm.transformIdExistsError', {
|
||||
defaultMessage: 'A transform with this ID already exists.',
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
]}
|
||||
>
|
||||
<EuiFieldText
|
||||
placeholder="transform ID"
|
||||
value={transformId}
|
||||
onChange={e => setTransformId(e.target.value)}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.transformIdInputAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Choose a unique transform ID.',
|
||||
}
|
||||
)}
|
||||
isInvalid={(!transformIdEmpty && !transformIdValid) || transformIdExists}
|
||||
data-test-subj="transformIdInput"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDetailsForm.transformDescriptionLabel', {
|
||||
defaultMessage: 'Transform description',
|
||||
})}
|
||||
helpText={i18n.translate('xpack.transform.stepDetailsForm.transformDescriptionHelpText', {
|
||||
defaultMessage: 'Optional descriptive text.',
|
||||
})}
|
||||
>
|
||||
<EuiFieldText
|
||||
placeholder="transform description"
|
||||
value={transformDescription}
|
||||
onChange={e => setTransformDescription(e.target.value)}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.transformDescriptionInputAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Choose an optional transform description.',
|
||||
}
|
||||
)}
|
||||
data-test-subj="transformDescriptionInput"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDetailsForm.destinationIndexLabel', {
|
||||
defaultMessage: 'Destination index',
|
||||
})}
|
||||
isInvalid={!indexNameEmpty && !indexNameValid}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
isInvalid={createIndexPattern && indexPatternTitleExists}
|
||||
error={
|
||||
createIndexPattern &&
|
||||
indexPatternTitleExists && [
|
||||
i18n.translate('xpack.transform.stepDetailsForm.indexPatternTitleError', {
|
||||
defaultMessage: 'An index pattern with this title already exists.',
|
||||
}),
|
||||
]
|
||||
}
|
||||
>
|
||||
<EuiSwitch
|
||||
name="transformCreateIndexPattern"
|
||||
label={i18n.translate('xpack.transform.stepCreateForm.createIndexPatternLabel', {
|
||||
defaultMessage: 'Create index pattern',
|
||||
})}
|
||||
checked={createIndexPattern === true}
|
||||
onChange={() => setCreateIndexPattern(!createIndexPattern)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
helpText={
|
||||
isContinuousModeAvailable === false
|
||||
? i18n.translate('xpack.transform.stepDetailsForm.continuousModeError', {
|
||||
defaultMessage: 'Continuous mode is not available for indices without date fields.',
|
||||
})
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<EuiSwitch
|
||||
name="transformContinuousMode"
|
||||
label={i18n.translate('xpack.transform.stepCreateForm.continuousModeLabel', {
|
||||
defaultMessage: 'Continuous mode',
|
||||
})}
|
||||
checked={isContinuousModeEnabled === true}
|
||||
onChange={() => setContinuousModeEnabled(!isContinuousModeEnabled)}
|
||||
disabled={isContinuousModeAvailable === false}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
{isContinuousModeEnabled && (
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDetailsForm.continuousModeDateFieldLabel', {
|
||||
defaultMessage: 'Date field',
|
||||
})}
|
||||
helpText={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.continuousModeDateFieldHelpText',
|
||||
helpText={
|
||||
indexNameExists &&
|
||||
i18n.translate('xpack.transform.stepDetailsForm.destinationIndexHelpText', {
|
||||
defaultMessage:
|
||||
'An index with this name already exists. Be aware that running this transform will modify this destination index.',
|
||||
})
|
||||
}
|
||||
error={
|
||||
!indexNameEmpty &&
|
||||
!indexNameValid && [
|
||||
<Fragment>
|
||||
{i18n.translate('xpack.transform.stepDetailsForm.destinationIndexInvalidError', {
|
||||
defaultMessage: 'Invalid destination index name.',
|
||||
})}
|
||||
<br />
|
||||
<EuiLink
|
||||
href={`https://www.elastic.co/guide/en/elasticsearch/reference/${metadata.branch}/indices-create-index.html#indices-create-index`}
|
||||
target="_blank"
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.destinationIndexInvalidErrorLink',
|
||||
{
|
||||
defaultMessage: 'Learn more about index name limitations.',
|
||||
}
|
||||
)}
|
||||
</EuiLink>
|
||||
</Fragment>,
|
||||
]
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
placeholder="destination index"
|
||||
value={destinationIndex}
|
||||
onChange={e => setDestinationIndex(e.target.value)}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.destinationIndexInputAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Select the date field that can be used to identify new documents.',
|
||||
defaultMessage: 'Choose a unique destination index name.',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<EuiSelect
|
||||
options={dateFieldNames.map(text => ({ text }))}
|
||||
value={continuousModeDateField}
|
||||
onChange={e => setContinuousModeDateField(e.target.value)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDetailsForm.continuousModeDelayLabel', {
|
||||
defaultMessage: 'Delay',
|
||||
isInvalid={!indexNameEmpty && !indexNameValid}
|
||||
data-test-subj="transformDestinationIndexInput"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
isInvalid={createIndexPattern && indexPatternTitleExists}
|
||||
error={
|
||||
createIndexPattern &&
|
||||
indexPatternTitleExists && [
|
||||
i18n.translate('xpack.transform.stepDetailsForm.indexPatternTitleError', {
|
||||
defaultMessage: 'An index pattern with this title already exists.',
|
||||
}),
|
||||
]
|
||||
}
|
||||
>
|
||||
<EuiSwitch
|
||||
name="transformCreateIndexPattern"
|
||||
label={i18n.translate('xpack.transform.stepCreateForm.createIndexPatternLabel', {
|
||||
defaultMessage: 'Create index pattern',
|
||||
})}
|
||||
isInvalid={!isContinuousModeDelayValid}
|
||||
error={
|
||||
!isContinuousModeDelayValid && [
|
||||
i18n.translate('xpack.transform.stepDetailsForm.continuousModeDelayError', {
|
||||
defaultMessage: 'Invalid delay format',
|
||||
}),
|
||||
]
|
||||
}
|
||||
helpText={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.continuousModeDelayHelpText',
|
||||
{
|
||||
defaultMessage: 'Time delay between current time and latest input data time.',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<EuiFieldText
|
||||
placeholder="delay"
|
||||
value={continuousModeDelay}
|
||||
onChange={e => setContinuousModeDelay(e.target.value)}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.continuousModeAriaLabel',
|
||||
checked={createIndexPattern === true}
|
||||
onChange={() => setCreateIndexPattern(!createIndexPattern)}
|
||||
data-test-subj="transformCreateIndexPatternSwitch"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
helpText={
|
||||
isContinuousModeAvailable === false
|
||||
? i18n.translate('xpack.transform.stepDetailsForm.continuousModeError', {
|
||||
defaultMessage:
|
||||
'Continuous mode is not available for indices without date fields.',
|
||||
})
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<EuiSwitch
|
||||
name="transformContinuousMode"
|
||||
label={i18n.translate('xpack.transform.stepCreateForm.continuousModeLabel', {
|
||||
defaultMessage: 'Continuous mode',
|
||||
})}
|
||||
checked={isContinuousModeEnabled === true}
|
||||
onChange={() => setContinuousModeEnabled(!isContinuousModeEnabled)}
|
||||
disabled={isContinuousModeAvailable === false}
|
||||
data-test-subj="transformContinuousModeSwitch"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
{isContinuousModeEnabled && (
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
label={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.continuousModeDateFieldLabel',
|
||||
{
|
||||
defaultMessage: 'Choose a delay.',
|
||||
defaultMessage: 'Date field',
|
||||
}
|
||||
)}
|
||||
helpText={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.continuousModeDateFieldHelpText',
|
||||
{
|
||||
defaultMessage:
|
||||
'Select the date field that can be used to identify new documents.',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<EuiSelect
|
||||
options={dateFieldNames.map(text => ({ text }))}
|
||||
value={continuousModeDateField}
|
||||
onChange={e => setContinuousModeDateField(e.target.value)}
|
||||
data-test-subj="transformContinuousDateFieldSelect"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDetailsForm.continuousModeDelayLabel', {
|
||||
defaultMessage: 'Delay',
|
||||
})}
|
||||
isInvalid={!isContinuousModeDelayValid}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</Fragment>
|
||||
)}
|
||||
</EuiForm>
|
||||
error={
|
||||
!isContinuousModeDelayValid && [
|
||||
i18n.translate('xpack.transform.stepDetailsForm.continuousModeDelayError', {
|
||||
defaultMessage: 'Invalid delay format',
|
||||
}),
|
||||
]
|
||||
}
|
||||
helpText={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.continuousModeDelayHelpText',
|
||||
{
|
||||
defaultMessage: 'Time delay between current time and latest input data time.',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<EuiFieldText
|
||||
placeholder="delay"
|
||||
value={continuousModeDelay}
|
||||
onChange={e => setContinuousModeDelay(e.target.value)}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.continuousModeAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Choose a delay.',
|
||||
}
|
||||
)}
|
||||
isInvalid={!isContinuousModeDelayValid}
|
||||
data-test-subj="transformContinuousDelayInput"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</Fragment>
|
||||
)}
|
||||
</EuiForm>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { Fragment, SFC } from 'react';
|
||||
import React, { SFC } from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
|
@ -33,7 +33,7 @@ export const StepDetailsSummary: SFC<StepDetailsExposedState> = React.memo(
|
|||
: '';
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div data-test-subj="transformStepDetailsSummary">
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.transform.stepDetailsSummary.transformIdLabel', {
|
||||
defaultMessage: 'Transform ID',
|
||||
|
@ -68,7 +68,7 @@ export const StepDetailsSummary: SFC<StepDetailsExposedState> = React.memo(
|
|||
<EuiFieldText defaultValue={continuousModeDateField} disabled={true} />
|
||||
</EuiFormRow>
|
||||
)}
|
||||
</Fragment>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -27,7 +27,13 @@ export const WizardNav: SFC<StepsNavProps> = ({
|
|||
<EuiFlexItem />
|
||||
{previous && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton disabled={!previousActive} onClick={previous} iconType="arrowLeft" size="s">
|
||||
<EuiButton
|
||||
disabled={!previousActive}
|
||||
onClick={previous}
|
||||
iconType="arrowLeft"
|
||||
size="s"
|
||||
data-test-subj="transformWizardNavButtonPrevious"
|
||||
>
|
||||
{i18n.translate('xpack.transform.wizard.previousStepButton', {
|
||||
defaultMessage: 'Previous',
|
||||
})}
|
||||
|
@ -36,7 +42,13 @@ export const WizardNav: SFC<StepsNavProps> = ({
|
|||
)}
|
||||
{next && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton disabled={!nextActive} onClick={next} iconType="arrowRight" size="s">
|
||||
<EuiButton
|
||||
disabled={!nextActive}
|
||||
onClick={next}
|
||||
iconType="arrowRight"
|
||||
size="s"
|
||||
data-test-subj="transformWizardNavButtonNext"
|
||||
>
|
||||
{i18n.translate('xpack.transform.wizard.nextStepButton', {
|
||||
defaultMessage: 'Next',
|
||||
})}
|
||||
|
|
|
@ -9,6 +9,7 @@ exports[`Transform: Transform List <TransformList /> Minimal initialization 1`]
|
|||
actions={
|
||||
Array [
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="transformCreateFirstButton"
|
||||
isDisabled={true}
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
|
|
|
@ -116,29 +116,34 @@ export const getColumns = (
|
|||
})
|
||||
}
|
||||
iconType={expandedRowItemIds.includes(item.config.id) ? 'arrowUp' : 'arrowDown'}
|
||||
data-test-subj="transformListRowDetailsToggle"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
field: TRANSFORM_LIST_COLUMN.ID,
|
||||
'data-test-subj': 'transformListColumnId',
|
||||
name: 'ID',
|
||||
sortable: true,
|
||||
truncateText: true,
|
||||
},
|
||||
{
|
||||
field: TRANSFORM_LIST_COLUMN.DESCRIPTION,
|
||||
'data-test-subj': 'transformListColumnDescription',
|
||||
name: i18n.translate('xpack.transform.description', { defaultMessage: 'Description' }),
|
||||
sortable: true,
|
||||
truncateText: true,
|
||||
},
|
||||
{
|
||||
field: TRANSFORM_LIST_COLUMN.CONFIG_SOURCE_INDEX,
|
||||
'data-test-subj': 'transformListColumnSourceIndex',
|
||||
name: i18n.translate('xpack.transform.sourceIndex', { defaultMessage: 'Source index' }),
|
||||
sortable: true,
|
||||
truncateText: true,
|
||||
},
|
||||
{
|
||||
field: TRANSFORM_LIST_COLUMN.CONFIG_DEST_INDEX,
|
||||
'data-test-subj': 'transformListColumnDestinationIndex',
|
||||
name: i18n.translate('xpack.transform.destinationIndex', {
|
||||
defaultMessage: 'Destination index',
|
||||
}),
|
||||
|
@ -147,6 +152,7 @@ export const getColumns = (
|
|||
},
|
||||
{
|
||||
name: i18n.translate('xpack.transform.status', { defaultMessage: 'Status' }),
|
||||
'data-test-subj': 'transformListColumnStatus',
|
||||
sortable: (item: TransformListRow) => item.stats.state,
|
||||
truncateText: true,
|
||||
render(item: TransformListRow) {
|
||||
|
@ -156,6 +162,7 @@ export const getColumns = (
|
|||
},
|
||||
{
|
||||
name: i18n.translate('xpack.transform.mode', { defaultMessage: 'Mode' }),
|
||||
'data-test-subj': 'transformListColumnMode',
|
||||
sortable: (item: TransformListRow) => item.mode,
|
||||
truncateText: true,
|
||||
render(item: TransformListRow) {
|
||||
|
@ -167,6 +174,7 @@ export const getColumns = (
|
|||
},
|
||||
{
|
||||
name: i18n.translate('xpack.transform.progress', { defaultMessage: 'Progress' }),
|
||||
'data-test-subj': 'transformListColumnProgress',
|
||||
sortable: (item: TransformListRow) => getTransformProgress(item) || 0,
|
||||
truncateText: true,
|
||||
render(item: TransformListRow) {
|
||||
|
@ -183,7 +191,13 @@ export const getColumns = (
|
|||
{isBatchTransform && (
|
||||
<Fragment>
|
||||
<EuiFlexItem style={{ width: '40px' }} grow={false}>
|
||||
<EuiProgress value={progress} max={100} color="primary" size="m">
|
||||
<EuiProgress
|
||||
value={progress}
|
||||
max={100}
|
||||
color="primary"
|
||||
size="m"
|
||||
data-test-subj="transformListProgress"
|
||||
>
|
||||
{progress}%
|
||||
</EuiProgress>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -219,7 +219,11 @@ export const TransformList: FC<Props> = ({
|
|||
</h2>
|
||||
}
|
||||
actions={[
|
||||
<EuiButtonEmpty onClick={onCreateTransform} isDisabled={disabled}>
|
||||
<EuiButtonEmpty
|
||||
onClick={onCreateTransform}
|
||||
isDisabled={disabled}
|
||||
data-test-subj="transformCreateFirstButton"
|
||||
>
|
||||
{i18n.translate('xpack.transform.list.emptyPromptButtonText', {
|
||||
defaultMessage: 'Create your first transform',
|
||||
})}
|
||||
|
@ -374,7 +378,7 @@ export const TransformList: FC<Props> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div data-test-subj="transformListTableContainer">
|
||||
<ProgressBar isLoading={isLoading || transformsLoading} />
|
||||
<TransformTable
|
||||
allowNeutralSort={false}
|
||||
|
@ -389,11 +393,18 @@ export const TransformList: FC<Props> = ({
|
|||
itemIdToExpandedRowMap={itemIdToExpandedRowMap}
|
||||
onTableChange={onTableChange}
|
||||
pagination={pagination}
|
||||
rowProps={item => ({
|
||||
'data-test-subj': `transformListRow row-${item.id}`,
|
||||
})}
|
||||
selection={selection}
|
||||
sorting={sorting}
|
||||
search={search}
|
||||
data-test-subj="transformTableTransforms"
|
||||
data-test-subj={
|
||||
isLoading || transformsLoading
|
||||
? 'transformListTable loading'
|
||||
: 'transformListTable loaded'
|
||||
}
|
||||
/>
|
||||
</Fragment>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -138,7 +138,11 @@ export const TransformManagement: FC = () => {
|
|||
</EuiPageBody>
|
||||
{isSearchSelectionVisible && (
|
||||
<EuiOverlayMask>
|
||||
<EuiModal onClose={onCloseModal} className="transformCreateTransformSearchDialog">
|
||||
<EuiModal
|
||||
onClose={onCloseModal}
|
||||
className="transformCreateTransformSearchDialog"
|
||||
data-test-subj="transformSelectSourceModal"
|
||||
>
|
||||
<SearchSelection onSearchSelected={onSearchSelected} />
|
||||
</EuiModal>
|
||||
</EuiOverlayMask>
|
||||
|
|
229
x-pack/test/functional/apps/transform/creation.ts
Normal file
229
x-pack/test/functional/apps/transform/creation.ts
Normal file
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
interface GroupByEntry {
|
||||
identifier: string;
|
||||
label: string;
|
||||
intervalLabel?: string;
|
||||
}
|
||||
|
||||
export default function({ getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const transform = getService('transform');
|
||||
|
||||
describe('creation', function() {
|
||||
this.tags(['smoke']);
|
||||
before(async () => {
|
||||
await esArchiver.load('ml/ecommerce');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('ml/ecommerce');
|
||||
await transform.api.cleanTransformIndices();
|
||||
});
|
||||
|
||||
const testDataList = [
|
||||
{
|
||||
suiteTitle: 'batch transform with terms+date_histogram groups and avg agg',
|
||||
source: 'ecommerce',
|
||||
groupByEntries: [
|
||||
{
|
||||
identifier: 'terms(category.keyword)',
|
||||
label: 'category.keyword',
|
||||
} as GroupByEntry,
|
||||
{
|
||||
identifier: 'date_histogram(order_date)',
|
||||
label: 'order_date',
|
||||
intervalLabel: '1m',
|
||||
} as GroupByEntry,
|
||||
],
|
||||
aggregationEntries: [
|
||||
{
|
||||
identifier: 'avg(products.base_price)',
|
||||
label: 'products.base_price.avg',
|
||||
},
|
||||
],
|
||||
transformId: `ec_1_${Date.now()}`,
|
||||
transformDescription:
|
||||
'ecommerce batch transform with groups terms(category.keyword) + date_histogram(order_date) 1m and aggregation avg(products.base_price)',
|
||||
get destinationIndex(): string {
|
||||
return `dest_${this.transformId}`;
|
||||
},
|
||||
expected: {
|
||||
row: {
|
||||
status: 'stopped',
|
||||
mode: 'batch',
|
||||
progress: '100',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const testData of testDataList) {
|
||||
describe(`${testData.suiteTitle}`, function() {
|
||||
after(async () => {
|
||||
await transform.api.deleteIndices(testData.destinationIndex);
|
||||
});
|
||||
|
||||
it('loads the home page', async () => {
|
||||
await transform.navigation.navigateTo();
|
||||
await transform.management.assertTransformListPageExists();
|
||||
});
|
||||
|
||||
it('displays the stats bar', async () => {
|
||||
await transform.management.assertTransformStatsBarExists();
|
||||
});
|
||||
|
||||
it('loads the source selection modal', async () => {
|
||||
await transform.management.startTransformCreation();
|
||||
});
|
||||
|
||||
it('selects the source data', async () => {
|
||||
await transform.sourceSelection.selectSource(testData.source);
|
||||
});
|
||||
|
||||
it('displays the define pivot step', async () => {
|
||||
await transform.wizard.assertDefineStepActive();
|
||||
});
|
||||
|
||||
it('loads the source index preview', async () => {
|
||||
await transform.wizard.assertSourceIndexPreviewLoaded();
|
||||
});
|
||||
|
||||
it('displays an empty pivot preview', async () => {
|
||||
await transform.wizard.assertPivotPreviewEmpty();
|
||||
});
|
||||
|
||||
it('displays the query input', async () => {
|
||||
await transform.wizard.assertQueryInputExists();
|
||||
await transform.wizard.assertQueryValue('');
|
||||
});
|
||||
|
||||
it('displays the advanced query editor switch', async () => {
|
||||
await transform.wizard.assertAdvancedQueryEditorSwitchExists();
|
||||
await transform.wizard.assertAdvancedQueryEditorSwitchCheckState(false);
|
||||
});
|
||||
|
||||
it('adds the group by entries', async () => {
|
||||
for (const [index, entry] of testData.groupByEntries.entries()) {
|
||||
await transform.wizard.assertGroupByInputExists();
|
||||
await transform.wizard.assertGroupByInputValue([]);
|
||||
await transform.wizard.addGroupByEntry(
|
||||
index,
|
||||
entry.identifier,
|
||||
entry.label,
|
||||
entry.intervalLabel
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('adds the aggregation entries', async () => {
|
||||
for (const [index, agg] of testData.aggregationEntries.entries()) {
|
||||
await transform.wizard.assertAggregationInputExists();
|
||||
await transform.wizard.assertAggregationInputValue([]);
|
||||
await transform.wizard.addAggregationEntry(index, agg.identifier, agg.label);
|
||||
}
|
||||
});
|
||||
|
||||
it('displays the advanced pivot editor switch', async () => {
|
||||
await transform.wizard.assertAdvancedPivotEditorSwitchExists();
|
||||
await transform.wizard.assertAdvancedPivotEditorSwitchCheckState(false);
|
||||
});
|
||||
|
||||
it('loads the pivot preview', async () => {
|
||||
await transform.wizard.assertPivotPreviewLoaded();
|
||||
});
|
||||
|
||||
it('loads the details step', async () => {
|
||||
await transform.wizard.advanceToDetailsStep();
|
||||
});
|
||||
|
||||
it('inputs the transform id', async () => {
|
||||
await transform.wizard.assertTransformIdInputExists();
|
||||
await transform.wizard.assertTransformIdValue('');
|
||||
await transform.wizard.setTransformId(testData.transformId);
|
||||
});
|
||||
|
||||
it('inputs the transform description', async () => {
|
||||
await transform.wizard.assertTransformDescriptionInputExists();
|
||||
await transform.wizard.assertTransformDescriptionValue('');
|
||||
await transform.wizard.setTransformDescription(testData.transformDescription);
|
||||
});
|
||||
|
||||
it('inputs the destination index', async () => {
|
||||
await transform.wizard.assertDestinationIndexInputExists();
|
||||
await transform.wizard.assertDestinationIndexValue('');
|
||||
await transform.wizard.setDestinationIndex(testData.destinationIndex);
|
||||
});
|
||||
|
||||
it('displays the create index pattern switch', async () => {
|
||||
await transform.wizard.assertCreateIndexPatternSwitchExists();
|
||||
await transform.wizard.assertCreateIndexPatternSwitchCheckState(true);
|
||||
});
|
||||
|
||||
it('displays the continuous mode switch', async () => {
|
||||
await transform.wizard.assertContinuousModeSwitchExists();
|
||||
await transform.wizard.assertContinuousModeSwitchCheckState(false);
|
||||
});
|
||||
|
||||
it('loads the create step', async () => {
|
||||
await transform.wizard.advanceToCreateStep();
|
||||
});
|
||||
|
||||
it('displays the create and start button', async () => {
|
||||
await transform.wizard.assertCreateAndStartButtonExists();
|
||||
});
|
||||
|
||||
it('displays the create button', async () => {
|
||||
await transform.wizard.assertCreateButtonExists();
|
||||
});
|
||||
|
||||
it('displays the copy to clipboard button', async () => {
|
||||
await transform.wizard.assertCreateAndStartButtonExists();
|
||||
});
|
||||
|
||||
it('creates the transform', async () => {
|
||||
await transform.wizard.createTransform();
|
||||
});
|
||||
|
||||
it('starts the transform and finishes processing', async () => {
|
||||
await transform.wizard.startTransform();
|
||||
await transform.wizard.waitForProgressBarComplete();
|
||||
});
|
||||
|
||||
it('returns to the management page', async () => {
|
||||
await transform.wizard.returnToManagement();
|
||||
});
|
||||
|
||||
it('displays the transforms table', async () => {
|
||||
await transform.management.assertTransformsTableExists();
|
||||
});
|
||||
|
||||
it('displays the created transform in the transform list', async () => {
|
||||
await transform.table.refreshTransformList();
|
||||
await transform.table.filterWithSearchString(testData.transformId);
|
||||
const rows = await transform.table.parseTransformTable();
|
||||
expect(rows.filter(row => row.id === testData.transformId)).to.have.length(1);
|
||||
});
|
||||
|
||||
it('job creation displays details for the created job in the job list', async () => {
|
||||
await transform.table.assertTransformRowFields(testData.transformId, {
|
||||
id: testData.transformId,
|
||||
description: testData.transformDescription,
|
||||
sourceIndex: testData.source,
|
||||
destinationIndex: testData.destinationIndex,
|
||||
status: testData.expected.row.status,
|
||||
mode: testData.expected.row.mode,
|
||||
progress: testData.expected.row.progress,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
14
x-pack/test/functional/apps/transform/index.ts
Normal file
14
x-pack/test/functional/apps/transform/index.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function({ loadTestFile }: FtrProviderContext) {
|
||||
describe('transform', function() {
|
||||
this.tags(['ciGroup9', 'transform']);
|
||||
|
||||
loadTestFile(require.resolve('./creation'));
|
||||
});
|
||||
}
|
|
@ -55,6 +55,7 @@ export default async function ({ readConfigFile }) {
|
|||
resolve(__dirname, './apps/snapshot_restore'),
|
||||
resolve(__dirname, './apps/cross_cluster_replication'),
|
||||
resolve(__dirname, './apps/remote_clusters'),
|
||||
resolve(__dirname, './apps/transform'),
|
||||
// This license_management file must be last because it is destructive.
|
||||
resolve(__dirname, './apps/license_management'),
|
||||
],
|
||||
|
@ -193,6 +194,10 @@ export default async function ({ readConfigFile }) {
|
|||
pathname: '/app/kibana',
|
||||
hash: '/management/elasticsearch/watcher/watches/',
|
||||
},
|
||||
transform: {
|
||||
pathname: '/app/kibana/',
|
||||
hash: '/management/elasticsearch/transform'
|
||||
}
|
||||
},
|
||||
|
||||
// choose where esArchiver should load archives from
|
||||
|
|
|
@ -47,6 +47,7 @@ import { UptimeProvider } from './uptime';
|
|||
import { InfraSourceConfigurationFormProvider } from './infra_source_configuration_form';
|
||||
import { InfraLogStreamProvider } from './infra_log_stream';
|
||||
import { MachineLearningProvider } from './ml';
|
||||
import { TransformProvider } from './transform';
|
||||
|
||||
import { SecurityServiceProvider, SpacesServiceProvider } from '../../common/services';
|
||||
|
||||
|
@ -89,4 +90,5 @@ export const services = {
|
|||
infraSourceConfigurationForm: InfraSourceConfigurationFormProvider,
|
||||
infraLogStream: InfraLogStreamProvider,
|
||||
ml: MachineLearningProvider,
|
||||
transform: TransformProvider,
|
||||
};
|
||||
|
|
34
x-pack/test/functional/services/transform.ts
Normal file
34
x-pack/test/functional/services/transform.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
import {
|
||||
TransformAPIProvider,
|
||||
TransformManagementProvider,
|
||||
TransformNavigationProvider,
|
||||
TransformSourceSelectionProvider,
|
||||
TransformTableProvider,
|
||||
TransformWizardProvider,
|
||||
} from './transform_ui';
|
||||
|
||||
export function TransformProvider(context: FtrProviderContext) {
|
||||
const api = TransformAPIProvider(context);
|
||||
const management = TransformManagementProvider(context);
|
||||
const navigation = TransformNavigationProvider(context);
|
||||
const sourceSelection = TransformSourceSelectionProvider(context);
|
||||
const table = TransformTableProvider(context);
|
||||
const wizard = TransformWizardProvider(context);
|
||||
|
||||
return {
|
||||
api,
|
||||
management,
|
||||
navigation,
|
||||
sourceSelection,
|
||||
table,
|
||||
wizard,
|
||||
};
|
||||
}
|
43
x-pack/test/functional/services/transform_ui/api.ts
Normal file
43
x-pack/test/functional/services/transform_ui/api.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function TransformAPIProvider({ getService }: FtrProviderContext) {
|
||||
const es = getService('legacyEs');
|
||||
const log = getService('log');
|
||||
const retry = getService('retry');
|
||||
|
||||
return {
|
||||
async deleteIndices(indices: string) {
|
||||
log.debug(`Deleting indices: '${indices}'...`);
|
||||
if ((await es.indices.exists({ index: indices, allowNoIndices: false })) === false) {
|
||||
log.debug(`Indices '${indices}' don't exist. Nothing to delete.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const deleteResponse = await es.indices.delete({
|
||||
index: indices,
|
||||
});
|
||||
expect(deleteResponse)
|
||||
.to.have.property('acknowledged')
|
||||
.eql(true, 'Response for delete request should be acknowledged');
|
||||
|
||||
await retry.waitForWithTimeout(`'${indices}' indices to be deleted`, 30 * 1000, async () => {
|
||||
if ((await es.indices.exists({ index: indices, allowNoIndices: false })) === false) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(`expected indices '${indices}' to be deleted`);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async cleanTransformIndices() {
|
||||
await this.deleteIndices('.transform-*');
|
||||
},
|
||||
};
|
||||
}
|
12
x-pack/test/functional/services/transform_ui/index.ts
Normal file
12
x-pack/test/functional/services/transform_ui/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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 { TransformAPIProvider } from './api';
|
||||
export { TransformManagementProvider } from './management';
|
||||
export { TransformNavigationProvider } from './navigation';
|
||||
export { TransformSourceSelectionProvider } from './source_selection';
|
||||
export { TransformTableProvider } from './transform_table';
|
||||
export { TransformWizardProvider } from './wizard';
|
42
x-pack/test/functional/services/transform_ui/management.ts
Normal file
42
x-pack/test/functional/services/transform_ui/management.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function TransformManagementProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
return {
|
||||
async assertTransformListPageExists() {
|
||||
await testSubjects.existOrFail('transformPageTransformList');
|
||||
},
|
||||
|
||||
async assertNoTransformsFoundMessageExists() {
|
||||
await testSubjects.existOrFail('transformNoTransformsFound');
|
||||
},
|
||||
|
||||
async assertTransformsTableExists() {
|
||||
await testSubjects.existOrFail('~transformListTable');
|
||||
},
|
||||
|
||||
async assertCreateNewTransformButtonExists() {
|
||||
await testSubjects.existOrFail('transformButtonCreate');
|
||||
},
|
||||
|
||||
async assertTransformStatsBarExists() {
|
||||
await testSubjects.existOrFail('transformStatsBar');
|
||||
},
|
||||
|
||||
async startTransformCreation() {
|
||||
if (await testSubjects.exists('transformNoTransformsFound')) {
|
||||
await testSubjects.click('transformCreateFirstButton');
|
||||
} else {
|
||||
await testSubjects.click('transformButtonCreate');
|
||||
}
|
||||
await testSubjects.existOrFail('transformSelectSourceModal');
|
||||
},
|
||||
};
|
||||
}
|
17
x-pack/test/functional/services/transform_ui/navigation.ts
Normal file
17
x-pack/test/functional/services/transform_ui/navigation.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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function TransformNavigationProvider({ getPageObjects }: FtrProviderContext) {
|
||||
const PageObjects = getPageObjects(['common']);
|
||||
|
||||
return {
|
||||
async navigateTo() {
|
||||
return await PageObjects.common.navigateToApp('transform');
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function TransformSourceSelectionProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
return {
|
||||
async assertSourceListContainsEntry(sourceName: string) {
|
||||
await testSubjects.existOrFail(`savedObjectTitle${sourceName}`);
|
||||
},
|
||||
|
||||
async filterSourceSelection(sourceName: string) {
|
||||
await testSubjects.setValue('savedObjectFinderSearchInput', sourceName, {
|
||||
clearWithKeyboard: true,
|
||||
});
|
||||
await this.assertSourceListContainsEntry(sourceName);
|
||||
},
|
||||
|
||||
async selectSource(sourceName: string) {
|
||||
await this.filterSourceSelection(sourceName);
|
||||
await testSubjects.clickWhenNotDisabled(`savedObjectTitle${sourceName}`);
|
||||
await testSubjects.existOrFail('transformPageCreateTransform');
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function TransformTableProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
return new (class TransformTable {
|
||||
public async parseTransformTable() {
|
||||
const table = await testSubjects.find('~transformListTable');
|
||||
const $ = await table.parseDomContent();
|
||||
const rows = [];
|
||||
|
||||
for (const tr of $.findTestSubjects('~transformListRow').toArray()) {
|
||||
const $tr = $(tr);
|
||||
|
||||
rows.push({
|
||||
id: $tr
|
||||
.findTestSubject('transformListColumnId')
|
||||
.find('.euiTableCellContent')
|
||||
.text()
|
||||
.trim(),
|
||||
description: $tr
|
||||
.findTestSubject('transformListColumnDescription')
|
||||
.find('.euiTableCellContent')
|
||||
.text()
|
||||
.trim(),
|
||||
sourceIndex: $tr
|
||||
.findTestSubject('transformListColumnSourceIndex')
|
||||
.find('.euiTableCellContent')
|
||||
.text()
|
||||
.trim(),
|
||||
destinationIndex: $tr
|
||||
.findTestSubject('transformListColumnDestinationIndex')
|
||||
.find('.euiTableCellContent')
|
||||
.text()
|
||||
.trim(),
|
||||
status: $tr
|
||||
.findTestSubject('transformListColumnStatus')
|
||||
.find('.euiTableCellContent')
|
||||
.text()
|
||||
.trim(),
|
||||
mode: $tr
|
||||
.findTestSubject('transformListColumnMode')
|
||||
.find('.euiTableCellContent')
|
||||
.text()
|
||||
.trim(),
|
||||
progress: $tr
|
||||
.findTestSubject('transformListColumnProgress')
|
||||
.findTestSubject('transformListProgress')
|
||||
.attr('value'),
|
||||
});
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
public async refreshTransformList() {
|
||||
await testSubjects.click('transformRefreshTransformListButton');
|
||||
await this.waitForTransformsToLoad();
|
||||
}
|
||||
|
||||
public async waitForTransformsToLoad() {
|
||||
await testSubjects.existOrFail('~transformListTable', { timeout: 60 * 1000 });
|
||||
await testSubjects.existOrFail('transformListTable loaded', { timeout: 30 * 1000 });
|
||||
}
|
||||
|
||||
public async filterWithSearchString(filter: string) {
|
||||
await this.waitForTransformsToLoad();
|
||||
const tableListContainer = await testSubjects.find('transformListTableContainer');
|
||||
const searchBarInput = await tableListContainer.findByClassName('euiFieldSearch');
|
||||
await searchBarInput.clearValueWithKeyboard();
|
||||
await searchBarInput.type(filter);
|
||||
}
|
||||
|
||||
public async assertTransformRowFields(transformId: string, expectedRow: object) {
|
||||
const rows = await this.parseTransformTable();
|
||||
const transformRow = rows.filter(row => row.id === transformId)[0];
|
||||
expect(transformRow).to.eql(expectedRow);
|
||||
}
|
||||
})();
|
||||
}
|
352
x-pack/test/functional/services/transform_ui/wizard.ts
Normal file
352
x-pack/test/functional/services/transform_ui/wizard.ts
Normal file
|
@ -0,0 +1,352 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function TransformWizardProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const comboBox = getService('comboBox');
|
||||
const retry = getService('retry');
|
||||
|
||||
return {
|
||||
async clickNextButton() {
|
||||
await testSubjects.existOrFail('transformWizardNavButtonNext');
|
||||
await testSubjects.clickWhenNotDisabled('transformWizardNavButtonNext');
|
||||
},
|
||||
|
||||
async assertDefineStepActive() {
|
||||
await testSubjects.existOrFail('transformStepDefineForm');
|
||||
},
|
||||
|
||||
async assertDefineSummaryExists() {
|
||||
await testSubjects.existOrFail('transformStepDefineSummary');
|
||||
},
|
||||
|
||||
async assertDetailsStepActive() {
|
||||
await testSubjects.existOrFail('transformStepDetailsForm');
|
||||
},
|
||||
|
||||
async assertDetailsSummaryExists() {
|
||||
await testSubjects.existOrFail('transformStepDetailsSummary');
|
||||
},
|
||||
|
||||
async assertCreateStepActive() {
|
||||
await testSubjects.existOrFail('transformStepCreateForm');
|
||||
},
|
||||
|
||||
async advanceToDetailsStep() {
|
||||
await this.clickNextButton();
|
||||
await this.assertDetailsStepActive();
|
||||
await this.assertDefineSummaryExists();
|
||||
},
|
||||
|
||||
async advanceToCreateStep() {
|
||||
await this.clickNextButton();
|
||||
await this.assertCreateStepActive();
|
||||
await this.assertDefineSummaryExists();
|
||||
await this.assertDetailsSummaryExists();
|
||||
},
|
||||
|
||||
async assertSourceIndexPreviewExists(subSelector?: string) {
|
||||
let selector = 'transformSourceIndexPreview';
|
||||
if (subSelector !== undefined) {
|
||||
selector = `${selector} ${subSelector}`;
|
||||
} else {
|
||||
selector = `~${selector}`;
|
||||
}
|
||||
await testSubjects.existOrFail(selector);
|
||||
},
|
||||
|
||||
async assertSourceIndexPreviewLoaded() {
|
||||
await this.assertSourceIndexPreviewExists('loaded');
|
||||
},
|
||||
|
||||
async assertPivotPreviewExists(subSelector?: string) {
|
||||
let selector = 'transformPivotPreview';
|
||||
if (subSelector !== undefined) {
|
||||
selector = `${selector} ${subSelector}`;
|
||||
} else {
|
||||
selector = `~${selector}`;
|
||||
}
|
||||
await testSubjects.existOrFail(selector);
|
||||
},
|
||||
|
||||
async assertPivotPreviewLoaded() {
|
||||
await this.assertPivotPreviewExists('loaded');
|
||||
},
|
||||
|
||||
async assertPivotPreviewEmpty() {
|
||||
await this.assertPivotPreviewExists('empty');
|
||||
},
|
||||
|
||||
async assertQueryInputExists() {
|
||||
await testSubjects.existOrFail('tarnsformQueryInput');
|
||||
},
|
||||
|
||||
async assertQueryValue(expectedQuery: string) {
|
||||
const actualQuery = await testSubjects.getVisibleText('tarnsformQueryInput');
|
||||
expect(actualQuery).to.eql(
|
||||
expectedQuery,
|
||||
`Query input text should be '${expectedQuery}' (got ${actualQuery})`
|
||||
);
|
||||
},
|
||||
|
||||
async assertAdvancedQueryEditorSwitchExists() {
|
||||
await testSubjects.existOrFail(`transformAdvancedQueryEditorSwitch`, { allowHidden: true });
|
||||
},
|
||||
|
||||
async assertAdvancedQueryEditorSwitchCheckState(expectedCheckState: boolean) {
|
||||
const actualCheckState = await testSubjects.isSelected('transformAdvancedQueryEditorSwitch');
|
||||
expect(actualCheckState).to.eql(
|
||||
expectedCheckState,
|
||||
`Advanced query editor switch check state should be ${expectedCheckState} (got ${actualCheckState})`
|
||||
);
|
||||
},
|
||||
|
||||
async assertGroupByInputExists() {
|
||||
await testSubjects.existOrFail('transformGroupBySelection > comboBoxInput');
|
||||
},
|
||||
|
||||
async assertGroupByInputValue(expectedIdentifier: string[]) {
|
||||
await retry.tryForTime(2000, async () => {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
'transformGroupBySelection > comboBoxInput'
|
||||
);
|
||||
expect(comboBoxSelectedOptions).to.eql(expectedIdentifier);
|
||||
});
|
||||
},
|
||||
|
||||
async assertGroupByEntryExists(
|
||||
index: number,
|
||||
expectedLabel: string,
|
||||
expectedIntervalLabel?: string
|
||||
) {
|
||||
await testSubjects.existOrFail(`transformGroupByEntry ${index}`);
|
||||
|
||||
const actualLabel = await testSubjects.getVisibleText(
|
||||
`transformGroupByEntry ${index} > transformGroupByEntryLabel`
|
||||
);
|
||||
expect(actualLabel).to.eql(
|
||||
expectedLabel,
|
||||
`Label for group by entry ${index} should be '${expectedLabel}' (got '${actualLabel}')`
|
||||
);
|
||||
|
||||
if (expectedIntervalLabel !== undefined) {
|
||||
const actualIntervalLabel = await testSubjects.getVisibleText(
|
||||
`transformGroupByEntry ${index} > transformGroupByEntryIntervalLabel`
|
||||
);
|
||||
expect(actualIntervalLabel).to.eql(
|
||||
expectedIntervalLabel,
|
||||
`Label for group by entry ${index} should be '${expectedIntervalLabel}' (got '${actualIntervalLabel}')`
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
async addGroupByEntry(
|
||||
index: number,
|
||||
identifier: string,
|
||||
expectedLabel: string,
|
||||
expectedIntervalLabel?: string
|
||||
) {
|
||||
await comboBox.set('transformGroupBySelection > comboBoxInput', identifier);
|
||||
await this.assertGroupByInputValue([]);
|
||||
await this.assertGroupByEntryExists(index, expectedLabel, expectedIntervalLabel);
|
||||
},
|
||||
|
||||
async assertAggregationInputExists() {
|
||||
await testSubjects.existOrFail('transformAggregationSelection > comboBoxInput');
|
||||
},
|
||||
|
||||
async assertAggregationInputValue(expectedIdentifier: string[]) {
|
||||
await retry.tryForTime(2000, async () => {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
'transformAggregationSelection > comboBoxInput'
|
||||
);
|
||||
expect(comboBoxSelectedOptions).to.eql(expectedIdentifier);
|
||||
});
|
||||
},
|
||||
|
||||
async assertAggregationEntryExists(index: number, expectedLabel: string) {
|
||||
await testSubjects.existOrFail(`transformAggregationEntry ${index}`);
|
||||
|
||||
const actualLabel = await testSubjects.getVisibleText(
|
||||
`transformAggregationEntry ${index} > transformAggregationEntryLabel`
|
||||
);
|
||||
expect(actualLabel).to.eql(
|
||||
expectedLabel,
|
||||
`Label for aggregation entry ${index} should be '${expectedLabel}' (got '${actualLabel}')`
|
||||
);
|
||||
},
|
||||
|
||||
async addAggregationEntry(index: number, identifier: string, expectedLabel: string) {
|
||||
await comboBox.set('transformAggregationSelection > comboBoxInput', identifier);
|
||||
await this.assertAggregationInputValue([]);
|
||||
await this.assertAggregationEntryExists(index, expectedLabel);
|
||||
},
|
||||
|
||||
async assertAdvancedPivotEditorSwitchExists() {
|
||||
await testSubjects.existOrFail(`transformAdvancedPivotEditorSwitch`, { allowHidden: true });
|
||||
},
|
||||
|
||||
async assertAdvancedPivotEditorSwitchCheckState(expectedCheckState: boolean) {
|
||||
const actualCheckState = await testSubjects.isSelected('transformAdvancedPivotEditorSwitch');
|
||||
expect(actualCheckState).to.eql(
|
||||
expectedCheckState,
|
||||
`Advanced pivot editor switch check state should be ${expectedCheckState} (got ${actualCheckState})`
|
||||
);
|
||||
},
|
||||
|
||||
async assertTransformIdInputExists() {
|
||||
await testSubjects.existOrFail('transformIdInput');
|
||||
},
|
||||
|
||||
async assertTransformIdValue(expectedValue: string) {
|
||||
const actualTransformId = await testSubjects.getAttribute('transformIdInput', 'value');
|
||||
expect(actualTransformId).to.eql(
|
||||
expectedValue,
|
||||
`Transform id input text should be ${expectedValue} (got ${actualTransformId})`
|
||||
);
|
||||
},
|
||||
|
||||
async setTransformId(transformId: string) {
|
||||
await testSubjects.setValue('transformIdInput', transformId, { clearWithKeyboard: true });
|
||||
await this.assertTransformIdValue(transformId);
|
||||
},
|
||||
|
||||
async assertTransformDescriptionInputExists() {
|
||||
await testSubjects.existOrFail('transformDescriptionInput');
|
||||
},
|
||||
|
||||
async assertTransformDescriptionValue(expectedValue: string) {
|
||||
const actualTransformDescription = await testSubjects.getAttribute(
|
||||
'transformDescriptionInput',
|
||||
'value'
|
||||
);
|
||||
expect(actualTransformDescription).to.eql(
|
||||
expectedValue,
|
||||
`Transform description input text should be ${expectedValue} (got ${actualTransformDescription})`
|
||||
);
|
||||
},
|
||||
|
||||
async setTransformDescription(transformDescription: string) {
|
||||
await testSubjects.setValue('transformDescriptionInput', transformDescription, {
|
||||
clearWithKeyboard: true,
|
||||
});
|
||||
await this.assertTransformDescriptionValue(transformDescription);
|
||||
},
|
||||
|
||||
async assertDestinationIndexInputExists() {
|
||||
await testSubjects.existOrFail('transformDestinationIndexInput');
|
||||
},
|
||||
|
||||
async assertDestinationIndexValue(expectedValue: string) {
|
||||
const actualDestinationIndex = await testSubjects.getAttribute(
|
||||
'transformDestinationIndexInput',
|
||||
'value'
|
||||
);
|
||||
expect(actualDestinationIndex).to.eql(
|
||||
expectedValue,
|
||||
`Destination index input text should be ${expectedValue} (got ${actualDestinationIndex})`
|
||||
);
|
||||
},
|
||||
|
||||
async setDestinationIndex(destinationIndex: string) {
|
||||
await testSubjects.setValue('transformDestinationIndexInput', destinationIndex, {
|
||||
clearWithKeyboard: true,
|
||||
});
|
||||
await this.assertDestinationIndexValue(destinationIndex);
|
||||
},
|
||||
|
||||
async assertCreateIndexPatternSwitchExists() {
|
||||
await testSubjects.existOrFail(`transformCreateIndexPatternSwitch`, { allowHidden: true });
|
||||
},
|
||||
|
||||
async assertCreateIndexPatternSwitchCheckState(expectedCheckState: boolean) {
|
||||
const actualCheckState = await testSubjects.isSelected('transformCreateIndexPatternSwitch');
|
||||
expect(actualCheckState).to.eql(
|
||||
expectedCheckState,
|
||||
`Create index pattern switch check state should be ${expectedCheckState} (got ${actualCheckState})`
|
||||
);
|
||||
},
|
||||
|
||||
async assertContinuousModeSwitchExists() {
|
||||
await testSubjects.existOrFail(`transformContinuousModeSwitch`, { allowHidden: true });
|
||||
},
|
||||
|
||||
async assertContinuousModeSwitchCheckState(expectedCheckState: boolean) {
|
||||
const actualCheckState = await testSubjects.isSelected('transformContinuousModeSwitch');
|
||||
expect(actualCheckState).to.eql(
|
||||
expectedCheckState,
|
||||
`Continuous mode switch check state should be ${expectedCheckState} (got ${actualCheckState})`
|
||||
);
|
||||
},
|
||||
|
||||
async assertCreateAndStartButtonExists() {
|
||||
await testSubjects.existOrFail(`transformWizardCreateAndStartButton`);
|
||||
},
|
||||
|
||||
async assertCreateButtonExists() {
|
||||
await testSubjects.existOrFail(`transformWizardCreateButton`);
|
||||
},
|
||||
|
||||
async assertCopyToClipboardButtonExists() {
|
||||
await testSubjects.existOrFail(`transformWizardCopyToClipboardButton`);
|
||||
},
|
||||
|
||||
async assertStartButtonExists() {
|
||||
await testSubjects.existOrFail(`transformWizardStartButton`);
|
||||
},
|
||||
|
||||
async assertManagementCardExists() {
|
||||
await testSubjects.existOrFail(`transformWizardCardManagement`);
|
||||
},
|
||||
|
||||
async returnToManagement() {
|
||||
await testSubjects.click('transformWizardCardManagement');
|
||||
await testSubjects.existOrFail('transformPageTransformList');
|
||||
},
|
||||
|
||||
async assertDiscoverCardExists() {
|
||||
await testSubjects.existOrFail(`transformWizardCardDiscover`);
|
||||
},
|
||||
|
||||
async assertProgressbarExists() {
|
||||
await testSubjects.existOrFail(`transformWizardProgressBar`);
|
||||
},
|
||||
|
||||
async waitForProgressBarComplete() {
|
||||
await retry.tryForTime(60 * 1000, async () => {
|
||||
const actualValue = await testSubjects.getAttribute('transformWizardProgressBar', 'value');
|
||||
if (actualValue === '100') {
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(`Expected progress bar value to be 100 (got ${actualValue})`);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async createTransform() {
|
||||
await testSubjects.click('transformWizardCreateButton');
|
||||
await this.assertStartButtonExists();
|
||||
await this.assertManagementCardExists();
|
||||
expect(await testSubjects.isEnabled('transformWizardCreateButton')).to.eql(
|
||||
false,
|
||||
'The create button should not be enabled any more'
|
||||
);
|
||||
},
|
||||
|
||||
async startTransform() {
|
||||
await testSubjects.click('transformWizardStartButton');
|
||||
await this.assertDiscoverCardExists();
|
||||
expect(await testSubjects.isEnabled('transformWizardStartButton')).to.eql(
|
||||
false,
|
||||
'The start button should not be enabled any more'
|
||||
);
|
||||
await this.assertProgressbarExists();
|
||||
},
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue