mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[ML] Transforms: Support to set destination ingest pipeline. (#123911)
* [ML] Support to set destination ingest pipeline. * [ML] Fix jest tests. * [ML] Fix permissions. * [ML] Use EuiComboBox to be able to empty input. * [ML] Add support to allow editing a transform destination ingest pipeline. * [ML] Fix setting ingest pipeline field to optional. * [ML] Fix translations. * [ML] Fix functional tests. * [ML] Fix unexposed form state. * [ML] Tweaks.
This commit is contained in:
parent
3efcf5218e
commit
815537afd4
15 changed files with 208 additions and 34 deletions
|
@ -8,6 +8,7 @@
|
|||
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
|
||||
import type { EsIndex } from '../types/es_index';
|
||||
import type { EsIngestPipeline } from '../types/es_ingest_pipeline';
|
||||
import { isPopulatedObject } from '../shared_imports';
|
||||
|
||||
// To be able to use the type guards on the client side, we need to make sure we don't import
|
||||
|
@ -70,6 +71,10 @@ export const isEsIndices = (arg: unknown): arg is EsIndex[] => {
|
|||
return Array.isArray(arg);
|
||||
};
|
||||
|
||||
export const isEsIngestPipelines = (arg: unknown): arg is EsIngestPipeline[] => {
|
||||
return Array.isArray(arg) && arg.every((d) => isPopulatedObject(d, ['name']));
|
||||
};
|
||||
|
||||
export const isEsSearchResponse = (arg: unknown): arg is estypes.SearchResponse => {
|
||||
return isPopulatedObject(arg, ['hits']);
|
||||
};
|
||||
|
|
|
@ -43,7 +43,7 @@ export const API_BASE_PATH = '/api/transform/';
|
|||
// In the UI additional privileges are required:
|
||||
// - kibana_admin (builtin)
|
||||
// - dest index: monitor (applied to df-*)
|
||||
// - cluster: monitor
|
||||
// - cluster: monitor, read_pipeline
|
||||
//
|
||||
// Note that users with kibana_admin can see all Kibana data views and saved searches
|
||||
// in the source selection modal when creating a transform, but the wizard will trigger
|
||||
|
@ -70,7 +70,7 @@ export const APP_GET_TRANSFORM_CLUSTER_PRIVILEGES = [
|
|||
'cluster.cluster:monitor/transform/stats/get',
|
||||
];
|
||||
|
||||
// Equivalent of capabilities.canGetTransform
|
||||
// Equivalent of capabilities.canCreateTransform
|
||||
export const APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES = [
|
||||
'cluster.cluster:monitor/transform/get',
|
||||
'cluster.cluster:monitor/transform/stats/get',
|
||||
|
|
13
x-pack/plugins/transform/common/types/es_ingest_pipeline.ts
Normal file
13
x-pack/plugins/transform/common/types/es_ingest_pipeline.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
// This interface doesn't cover a full ingest pipeline spec,
|
||||
// just what's necessary to make it work in the transform creation wizard.
|
||||
// The full interface can be found in x-pack/plugins/ingest_pipelines/common/types.ts
|
||||
export interface EsIngestPipeline {
|
||||
name: string;
|
||||
}
|
|
@ -237,6 +237,7 @@ describe('Transform: Common', () => {
|
|||
transformSettingsMaxPageSearchSize: 100,
|
||||
transformSettingsDocsPerSecond: 400,
|
||||
destinationIndex: 'the-destination-index',
|
||||
destinationIngestPipeline: 'the-destination-ingest-pipeline',
|
||||
touched: true,
|
||||
valid: true,
|
||||
};
|
||||
|
@ -249,7 +250,7 @@ describe('Transform: Common', () => {
|
|||
|
||||
expect(request).toEqual({
|
||||
description: 'the-transform-description',
|
||||
dest: { index: 'the-destination-index' },
|
||||
dest: { index: 'the-destination-index', pipeline: 'the-destination-ingest-pipeline' },
|
||||
frequency: '1m',
|
||||
pivot: {
|
||||
aggregations: { 'the-agg-agg-name': { avg: { field: 'the-agg-field' } } },
|
||||
|
@ -315,6 +316,7 @@ describe('Transform: Common', () => {
|
|||
transformSettingsMaxPageSearchSize: 100,
|
||||
transformSettingsDocsPerSecond: 400,
|
||||
destinationIndex: 'the-destination-index',
|
||||
destinationIngestPipeline: 'the-destination-ingest-pipeline',
|
||||
touched: true,
|
||||
valid: true,
|
||||
};
|
||||
|
@ -327,7 +329,7 @@ describe('Transform: Common', () => {
|
|||
|
||||
expect(request).toEqual({
|
||||
description: 'the-transform-description',
|
||||
dest: { index: 'the-destination-index' },
|
||||
dest: { index: 'the-destination-index', pipeline: 'the-destination-ingest-pipeline' },
|
||||
frequency: '1m',
|
||||
pivot: {
|
||||
aggregations: { 'the-agg-agg-name': { avg: { field: 'the-agg-field' } } },
|
||||
|
|
|
@ -219,6 +219,10 @@ export const getCreateTransformRequestBody = (
|
|||
: {}),
|
||||
dest: {
|
||||
index: transformDetailsState.destinationIndex,
|
||||
// conditionally add optional ingest pipeline
|
||||
...(transformDetailsState.destinationIngestPipeline !== ''
|
||||
? { pipeline: transformDetailsState.destinationIngestPipeline }
|
||||
: {}),
|
||||
},
|
||||
// conditionally add continuous mode config
|
||||
...(transformDetailsState.isContinuousModeEnabled
|
||||
|
|
|
@ -9,7 +9,7 @@ import { useMemo } from 'react';
|
|||
|
||||
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
|
||||
import { HttpFetchError } from 'kibana/public';
|
||||
import type { HttpFetchError } from 'kibana/public';
|
||||
|
||||
import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/public';
|
||||
|
||||
|
@ -47,9 +47,10 @@ import type {
|
|||
PostTransformsUpdateResponseSchema,
|
||||
} from '../../../common/api_schemas/update_transforms';
|
||||
import type { GetTransformsStatsResponseSchema } from '../../../common/api_schemas/transforms_stats';
|
||||
import { TransformId } from '../../../common/types/transform';
|
||||
import type { TransformId } from '../../../common/types/transform';
|
||||
import { API_BASE_PATH } from '../../../common/constants';
|
||||
import { EsIndex } from '../../../common/types/es_index';
|
||||
import type { EsIndex } from '../../../common/types/es_index';
|
||||
import type { EsIngestPipeline } from '../../../common/types/es_ingest_pipeline';
|
||||
|
||||
import { useAppDependencies } from '../app_dependencies';
|
||||
|
||||
|
@ -217,6 +218,13 @@ export const useApi = () => {
|
|||
return e;
|
||||
}
|
||||
},
|
||||
async getEsIngestPipelines(): Promise<EsIngestPipeline[] | HttpFetchError> {
|
||||
try {
|
||||
return await http.get('/api/ingest_pipelines');
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
},
|
||||
async getHistogramsForFields(
|
||||
indexPatternTitle: string,
|
||||
fields: FieldHistogramRequestConfig[],
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import type { TransformConfigUnion, TransformId } from '../../../../../../common/types/transform';
|
||||
|
||||
export type EsIndexName = string;
|
||||
export type EsIngestPipelineName = string;
|
||||
export type IndexPatternTitle = string;
|
||||
|
||||
export interface StepDetailsExposedState {
|
||||
|
@ -15,6 +16,7 @@ export interface StepDetailsExposedState {
|
|||
continuousModeDelay: string;
|
||||
createIndexPattern: boolean;
|
||||
destinationIndex: EsIndexName;
|
||||
destinationIngestPipeline: EsIngestPipelineName;
|
||||
isContinuousModeEnabled: boolean;
|
||||
isRetentionPolicyEnabled: boolean;
|
||||
retentionPolicyDateField: string;
|
||||
|
@ -48,6 +50,7 @@ export function getDefaultStepDetailsState(): StepDetailsExposedState {
|
|||
transformFrequency: defaultTransformFrequency,
|
||||
transformSettingsMaxPageSearchSize: defaultTransformSettingsMaxPageSearchSize,
|
||||
destinationIndex: '',
|
||||
destinationIngestPipeline: '',
|
||||
touched: false,
|
||||
valid: false,
|
||||
indexPatternTimeField: undefined,
|
||||
|
@ -73,6 +76,11 @@ export function applyTransformConfigToDetailsState(
|
|||
state.transformDescription = transformConfig.description;
|
||||
}
|
||||
|
||||
// Ingest Pipeline
|
||||
if (transformConfig.dest.pipeline !== undefined) {
|
||||
state.destinationIngestPipeline = transformConfig.dest.pipeline;
|
||||
}
|
||||
|
||||
// Frequency
|
||||
if (transformConfig.frequency !== undefined) {
|
||||
state.transformFrequency = transformConfig.frequency;
|
||||
|
|
|
@ -12,6 +12,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
|
||||
import {
|
||||
EuiAccordion,
|
||||
EuiComboBox,
|
||||
EuiLink,
|
||||
EuiSwitch,
|
||||
EuiFieldText,
|
||||
|
@ -28,6 +29,7 @@ import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_reac
|
|||
|
||||
import {
|
||||
isEsIndices,
|
||||
isEsIngestPipelines,
|
||||
isPostTransformsPreviewResponseSchema,
|
||||
} from '../../../../../../common/api_schemas/type_guards';
|
||||
import { TransformId } from '../../../../../../common/types/transform';
|
||||
|
@ -82,8 +84,12 @@ export const StepDetailsForm: FC<StepDetailsFormProps> = React.memo(
|
|||
const [destinationIndex, setDestinationIndex] = useState<EsIndexName>(
|
||||
defaults.destinationIndex
|
||||
);
|
||||
const [destinationIngestPipeline, setDestinationIngestPipeline] = useState<string>(
|
||||
defaults.destinationIngestPipeline
|
||||
);
|
||||
const [transformIds, setTransformIds] = useState<TransformId[]>([]);
|
||||
const [indexNames, setIndexNames] = useState<EsIndexName[]>([]);
|
||||
const [ingestPipelineNames, setIngestPipelineNames] = useState<string[]>([]);
|
||||
|
||||
const canCreateDataView = useMemo(
|
||||
() =>
|
||||
|
@ -180,7 +186,10 @@ export const StepDetailsForm: FC<StepDetailsFormProps> = React.memo(
|
|||
setTransformIds(resp.transforms.map((transform) => transform.id));
|
||||
}
|
||||
|
||||
const indices = await api.getEsIndices();
|
||||
const [indices, ingestPipelines] = await Promise.all([
|
||||
api.getEsIndices(),
|
||||
api.getEsIngestPipelines(),
|
||||
]);
|
||||
|
||||
if (isEsIndices(indices)) {
|
||||
setIndexNames(indices.map((index) => index.name));
|
||||
|
@ -200,6 +209,24 @@ export const StepDetailsForm: FC<StepDetailsFormProps> = React.memo(
|
|||
});
|
||||
}
|
||||
|
||||
if (isEsIngestPipelines(ingestPipelines)) {
|
||||
setIngestPipelineNames(ingestPipelines.map(({ name }) => name));
|
||||
} else {
|
||||
toastNotifications.addDanger({
|
||||
title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingIngestPipelines', {
|
||||
defaultMessage: 'An error occurred getting the existing ingest pipeline names:',
|
||||
}),
|
||||
text: toMountPoint(
|
||||
<ToastNotificationText
|
||||
overlays={overlays}
|
||||
theme={theme}
|
||||
text={getErrorMessage(ingestPipelines)}
|
||||
/>,
|
||||
{ theme$: theme.theme$ }
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
setIndexPatternTitles(await deps.data.indexPatterns.getTitles());
|
||||
} catch (e) {
|
||||
|
@ -311,6 +338,7 @@ export const StepDetailsForm: FC<StepDetailsFormProps> = React.memo(
|
|||
transformSettingsMaxPageSearchSize,
|
||||
transformSettingsDocsPerSecond,
|
||||
destinationIndex,
|
||||
destinationIngestPipeline,
|
||||
touched: true,
|
||||
valid,
|
||||
indexPatternTimeField,
|
||||
|
@ -331,6 +359,7 @@ export const StepDetailsForm: FC<StepDetailsFormProps> = React.memo(
|
|||
transformFrequency,
|
||||
transformSettingsMaxPageSearchSize,
|
||||
destinationIndex,
|
||||
destinationIngestPipeline,
|
||||
valid,
|
||||
indexPatternTimeField,
|
||||
/* eslint-enable react-hooks/exhaustive-deps */
|
||||
|
@ -443,6 +472,37 @@ export const StepDetailsForm: FC<StepDetailsFormProps> = React.memo(
|
|||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
{ingestPipelineNames.length > 0 && (
|
||||
<EuiFormRow
|
||||
label={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.destinationIngestPipelineLabel',
|
||||
{
|
||||
defaultMessage: 'Destination ingest pipeline',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<EuiComboBox
|
||||
data-test-subj="transformDestinationPipelineSelect"
|
||||
aria-label={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.destinationIngestPipelineAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Select an ingest pipeline',
|
||||
}
|
||||
)}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.destinationIngestPipelineComboBoxPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Select an ingest pipeline',
|
||||
}
|
||||
)}
|
||||
singleSelection={{ asPlainText: true }}
|
||||
options={ingestPipelineNames.map((label: string) => ({ label }))}
|
||||
selectedOptions={[{ label: destinationIngestPipeline }]}
|
||||
onChange={(options) => setDestinationIngestPipeline(options[0]?.label ?? '')}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
|
||||
{stepDefineState.transformFunction === TRANSFORM_FUNCTION.LATEST ? (
|
||||
<>
|
||||
<EuiSpacer size={'m'} />
|
||||
|
|
|
@ -7,13 +7,16 @@
|
|||
|
||||
import React, { FC, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { EuiForm, EuiAccordion, EuiSpacer, EuiSelect, EuiFormRow } from '@elastic/eui';
|
||||
import { EuiComboBox, EuiForm, EuiAccordion, EuiSpacer, EuiSelect, EuiFormRow } from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { isEsIngestPipelines } from '../../../../../../common/api_schemas/type_guards';
|
||||
import { EditTransformFlyoutFormTextInput } from './edit_transform_flyout_form_text_input';
|
||||
import { UseEditTransformFlyoutReturnType } from './use_edit_transform_flyout';
|
||||
import { useAppDependencies } from '../../../../app_dependencies';
|
||||
import { useApi } from '../../../../hooks/use_api';
|
||||
|
||||
import { KBN_FIELD_TYPES } from '../../../../../../../../../src/plugins/data/common';
|
||||
|
||||
interface EditTransformFlyoutFormProps {
|
||||
|
@ -27,9 +30,11 @@ export const EditTransformFlyoutForm: FC<EditTransformFlyoutFormProps> = ({
|
|||
}) => {
|
||||
const formFields = state.formFields;
|
||||
const [dateFieldNames, setDateFieldNames] = useState<string[]>([]);
|
||||
const [ingestPipelineNames, setIngestPipelineNames] = useState<string[]>([]);
|
||||
|
||||
const appDeps = useAppDependencies();
|
||||
const indexPatternsClient = appDeps.data.indexPatterns;
|
||||
const api = useApi();
|
||||
|
||||
useEffect(
|
||||
function getDateFields() {
|
||||
|
@ -54,6 +59,25 @@ export const EditTransformFlyoutForm: FC<EditTransformFlyoutFormProps> = ({
|
|||
[indexPatternId, indexPatternsClient]
|
||||
);
|
||||
|
||||
useEffect(function fetchPipelinesOnMount() {
|
||||
let unmounted = false;
|
||||
|
||||
async function getIngestPipelineNames() {
|
||||
const ingestPipelines = await api.getEsIngestPipelines();
|
||||
|
||||
if (!unmounted && isEsIngestPipelines(ingestPipelines)) {
|
||||
setIngestPipelineNames(ingestPipelines.map(({ name }) => name));
|
||||
}
|
||||
}
|
||||
|
||||
getIngestPipelineNames();
|
||||
|
||||
return () => {
|
||||
unmounted = true;
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const retentionDateFieldOptions = useMemo(() => {
|
||||
return Array.isArray(dateFieldNames)
|
||||
? dateFieldNames.map((text: string) => ({ text, value: text }))
|
||||
|
@ -120,18 +144,61 @@ export const EditTransformFlyoutForm: FC<EditTransformFlyoutFormProps> = ({
|
|||
value={formFields.destinationIndex.value}
|
||||
/>
|
||||
|
||||
<EditTransformFlyoutFormTextInput
|
||||
dataTestSubj="transformEditFlyoutDestinationPipelineInput"
|
||||
errorMessages={formFields.destinationPipeline.errorMessages}
|
||||
label={i18n.translate(
|
||||
'xpack.transform.transformList.editFlyoutFormDestinationPipelineLabel',
|
||||
{
|
||||
defaultMessage: 'Pipeline',
|
||||
}
|
||||
)}
|
||||
onChange={(value) => dispatch({ field: 'destinationPipeline', value })}
|
||||
value={formFields.destinationPipeline.value}
|
||||
/>
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<div data-test-subj="transformEditAccordionIngestPipelineContent">
|
||||
{
|
||||
// If the list of ingest pipelines is not available
|
||||
// gracefully defaults to text input
|
||||
ingestPipelineNames ? (
|
||||
<EuiFormRow
|
||||
label={i18n.translate(
|
||||
'xpack.transform.transformList.editFlyoutFormDestinationIngestPipelineLabel',
|
||||
{
|
||||
defaultMessage: 'Ingest Pipeline',
|
||||
}
|
||||
)}
|
||||
isInvalid={formFields.destinationIngestPipeline.errorMessages.length > 0}
|
||||
error={formFields.destinationIngestPipeline.errorMessages}
|
||||
>
|
||||
<EuiComboBox
|
||||
data-test-subj="transformEditFlyoutDestinationIngestPipelineFieldSelect"
|
||||
aria-label={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.editFlyoutFormDestinationIngestPipelineFieldSelectAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Select an ingest pipeline',
|
||||
}
|
||||
)}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.editFlyoutFormDestinationIngestPipelineFieldSelectPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Select an ingest pipeline',
|
||||
}
|
||||
)}
|
||||
singleSelection={{ asPlainText: true }}
|
||||
options={ingestPipelineNames.map((label: string) => ({ label }))}
|
||||
selectedOptions={[{ label: formFields.destinationIngestPipeline.value }]}
|
||||
onChange={(o) =>
|
||||
dispatch({ field: 'destinationIngestPipeline', value: o[0]?.label ?? '' })
|
||||
}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
) : (
|
||||
<EditTransformFlyoutFormTextInput
|
||||
dataTestSubj="transformEditFlyoutDestinationIngestPipelineInput"
|
||||
errorMessages={formFields.destinationIngestPipeline.errorMessages}
|
||||
label={i18n.translate(
|
||||
'xpack.transform.transformList.editFlyoutFormDestinationIngestPipelineLabel',
|
||||
{
|
||||
defaultMessage: 'Ingest Pipeline',
|
||||
}
|
||||
)}
|
||||
onChange={(value) => dispatch({ field: 'destinationIngestPipeline', value })}
|
||||
value={formFields.destinationIngestPipeline.value}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</EuiAccordion>
|
||||
|
||||
|
|
|
@ -30,11 +30,11 @@ import {
|
|||
|
||||
// The outer most level reducer defines a flat structure of names for form fields.
|
||||
// This is a flat structure regardless of whether the final request object will be nested.
|
||||
// For example, `destinationIndex` and `destinationPipeline` will later be nested under `dest`.
|
||||
// For example, `destinationIndex` and `destinationIngestPipeline` will later be nested under `dest`.
|
||||
type EditTransformFormFields =
|
||||
| 'description'
|
||||
| 'destinationIndex'
|
||||
| 'destinationPipeline'
|
||||
| 'destinationIngestPipeline'
|
||||
| 'frequency'
|
||||
| 'docsPerSecond'
|
||||
| 'maxPageSearchSize'
|
||||
|
@ -300,12 +300,18 @@ export const getDefaultState = (config: TransformConfigUnion): EditTransformFlyo
|
|||
|
||||
// dest.*
|
||||
destinationIndex: initializeField('destinationIndex', 'dest.index', config, {
|
||||
dependsOn: ['destinationPipeline'],
|
||||
dependsOn: ['destinationIngestPipeline'],
|
||||
isOptional: false,
|
||||
}),
|
||||
destinationPipeline: initializeField('destinationPipeline', 'dest.pipeline', config, {
|
||||
dependsOn: ['destinationIndex'],
|
||||
}),
|
||||
destinationIngestPipeline: initializeField(
|
||||
'destinationIngestPipeline',
|
||||
'dest.pipeline',
|
||||
config,
|
||||
{
|
||||
dependsOn: ['destinationIndex'],
|
||||
isOptional: true,
|
||||
}
|
||||
),
|
||||
|
||||
// settings.*
|
||||
docsPerSecond: initializeField('docsPerSecond', 'settings.docs_per_second', config, {
|
||||
|
|
|
@ -25981,7 +25981,7 @@
|
|||
"xpack.transform.transformList.editFlyoutFormDescriptionLabel": "説明",
|
||||
"xpack.transform.transformList.editFlyoutFormDestinationButtonContent": "ディスティネーション構成",
|
||||
"xpack.transform.transformList.editFlyoutFormDestinationIndexLabel": "デスティネーションインデックス",
|
||||
"xpack.transform.transformList.editFlyoutFormDestinationPipelineLabel": "パイプライン",
|
||||
"xpack.transform.transformList.editFlyoutFormDestinationIngestPipelineLabel": "パイプライン",
|
||||
"xpack.transform.transformList.editFlyoutFormDocsPerSecondHelptext": "スロットリングを有効にするには、毎秒入力するドキュメントの上限を設定します。",
|
||||
"xpack.transform.transformList.editFlyoutFormDocsPerSecondLabel": "毎秒あたりのドキュメント",
|
||||
"xpack.transform.transformList.editFlyoutFormFrequencyHelpText": "変換が連続実行されているときにソースインデックスで変更を確認する間の間隔。また、変換が検索またはインデックス中に一時障害が発生した場合に、再試行する間隔も決定します。最小値は1秒で、最大値は1時間です。",
|
||||
|
|
|
@ -26442,7 +26442,7 @@
|
|||
"xpack.transform.transformList.editFlyoutFormDescriptionLabel": "描述",
|
||||
"xpack.transform.transformList.editFlyoutFormDestinationButtonContent": "目标配置",
|
||||
"xpack.transform.transformList.editFlyoutFormDestinationIndexLabel": "目标索引",
|
||||
"xpack.transform.transformList.editFlyoutFormDestinationPipelineLabel": "管道",
|
||||
"xpack.transform.transformList.editFlyoutFormDestinationIngestPipelineLabel": "管道",
|
||||
"xpack.transform.transformList.editFlyoutFormDocsPerSecondHelptext": "要启用节流,请设置每秒要输入的文档限值。",
|
||||
"xpack.transform.transformList.editFlyoutFormDocsPerSecondLabel": "每秒文档数",
|
||||
"xpack.transform.transformList.editFlyoutFormFrequencyHelpText": "在转换不间断地执行时检查源索引更改的时间间隔。还确定在转换搜索或索引时发生暂时失败时的重试时间间隔。最小值为 1 秒,最大值为 1 小时。",
|
||||
|
|
|
@ -151,10 +151,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await transform.testExecution.logTestStep('should have the destination inputs enabled');
|
||||
await transform.editFlyout.openTransformEditAccordionDestinationSettings();
|
||||
await transform.editFlyout.assertTransformEditFlyoutInputEnabled('DestinationIndex', true);
|
||||
await transform.editFlyout.assertTransformEditFlyoutInputEnabled(
|
||||
'DestinationPipeline',
|
||||
true
|
||||
);
|
||||
await transform.editFlyout.assertTransformEditFlyoutIngestPipelineFieldSelectExists();
|
||||
|
||||
await transform.testExecution.logTestStep(
|
||||
'should have the retention policy inputs enabled'
|
||||
|
|
|
@ -37,6 +37,10 @@ export function TransformEditFlyoutProvider({ getService }: FtrProviderContext)
|
|||
);
|
||||
},
|
||||
|
||||
async assertTransformEditFlyoutIngestPipelineFieldSelectExists() {
|
||||
await testSubjects.existOrFail(`transformEditFlyoutDestinationIngestPipelineFieldSelect`);
|
||||
},
|
||||
|
||||
async assertTransformEditFlyoutRetentionPolicyFieldSelectEnabled(expectedValue: boolean) {
|
||||
await testSubjects.existOrFail(`transformEditFlyoutRetentionPolicyFieldSelect`, {
|
||||
timeout: 1000,
|
||||
|
|
|
@ -45,7 +45,7 @@ export function TransformSecurityCommonProvider({ getService }: FtrProviderConte
|
|||
{
|
||||
name: 'transform_ui_extras',
|
||||
elasticsearch: {
|
||||
cluster: ['monitor'],
|
||||
cluster: ['monitor', 'read_pipeline'],
|
||||
},
|
||||
kibana: [],
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue