mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
[ML] Transforms: Adds per-transform setting for num_failure_retries to creation wizard and edit flyout and authorization info (#135486)
* [ML] Add optional num_failure_retries setting in creation wizard and edit flyout * [ML] Fix logic for input validation * Fix types & i18n * Change to integerRangeMinus1To100 * Fix clone * Fix test * Update text * [ML] Add functional tests * [ML] Add functional tests for editting * [ML] Update translations * [ML] Surface num failure retries to stats * [ML] Add authorization info * [ML] Fix extra period * [ML] Move numberValidator to its own package * [ML] Add tests for cloning * [ML] Update logic + add unit tests * [ML] Fix expected value
This commit is contained in:
parent
dd7d92f282
commit
6b51010141
24 changed files with 461 additions and 92 deletions
|
@ -8,3 +8,5 @@
|
|||
export { buildSamplerAggregation } from './build_sampler_aggregation';
|
||||
export { getAggIntervals } from './get_agg_intervals';
|
||||
export { getSamplerAggregationsResponsePath } from './get_sampler_aggregations_response_path';
|
||||
export type { NumberValidationResult } from './validate_number';
|
||||
export { numberValidator } from './validate_number';
|
||||
|
|
42
x-pack/packages/ml/agg_utils/src/validate_number.test.ts
Normal file
42
x-pack/packages/ml/agg_utils/src/validate_number.test.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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { numberValidator } from '@kbn/ml-agg-utils';
|
||||
|
||||
describe('numberValidator', () => {
|
||||
it('should only allow integers above zero', () => {
|
||||
const integerOnlyValidator = numberValidator({ min: 1, integerOnly: true });
|
||||
// invalid
|
||||
expect(integerOnlyValidator(0)).toMatchObject({ min: true });
|
||||
expect(integerOnlyValidator(0.1)).toMatchObject({ integerOnly: true });
|
||||
|
||||
// valid
|
||||
expect(integerOnlyValidator(1)).toStrictEqual(null);
|
||||
expect(integerOnlyValidator(100)).toStrictEqual(null);
|
||||
});
|
||||
|
||||
it('should not allow value greater than max', () => {
|
||||
const integerOnlyValidator = numberValidator({ min: 1, max: 8, integerOnly: true });
|
||||
// invalid
|
||||
expect(integerOnlyValidator(10)).toMatchObject({ max: true });
|
||||
expect(integerOnlyValidator(11.1)).toMatchObject({ integerOnly: true, max: true });
|
||||
|
||||
// valid
|
||||
expect(integerOnlyValidator(6)).toStrictEqual(null);
|
||||
});
|
||||
|
||||
it('should allow non-integers', () => {
|
||||
const integerOnlyValidator = numberValidator({ min: 1, max: 8, integerOnly: false });
|
||||
// invalid
|
||||
expect(integerOnlyValidator(10)).toMatchObject({ max: true });
|
||||
expect(integerOnlyValidator(11.1)).toMatchObject({ max: true });
|
||||
|
||||
// valid
|
||||
expect(integerOnlyValidator(6)).toStrictEqual(null);
|
||||
expect(integerOnlyValidator(6.6)).toStrictEqual(null);
|
||||
});
|
||||
});
|
48
x-pack/packages/ml/agg_utils/src/validate_number.ts
Normal file
48
x-pack/packages/ml/agg_utils/src/validate_number.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { isPopulatedObject } from '@kbn/ml-is-populated-object';
|
||||
|
||||
export interface NumberValidationResult {
|
||||
min: boolean;
|
||||
max: boolean;
|
||||
integerOnly: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if a number is greater than a specified minimum & lesser than specified maximum
|
||||
*/
|
||||
export function numberValidator(conditions?: {
|
||||
min?: number;
|
||||
max?: number;
|
||||
integerOnly?: boolean;
|
||||
}) {
|
||||
if (
|
||||
conditions?.min !== undefined &&
|
||||
conditions.max !== undefined &&
|
||||
conditions.min > conditions.max
|
||||
) {
|
||||
throw new Error('Invalid validator conditions');
|
||||
}
|
||||
|
||||
return (value: number): NumberValidationResult | null => {
|
||||
const result = {} as NumberValidationResult;
|
||||
if (conditions?.min !== undefined && value < conditions.min) {
|
||||
result.min = true;
|
||||
}
|
||||
if (conditions?.max !== undefined && value > conditions.max) {
|
||||
result.max = true;
|
||||
}
|
||||
if (!!conditions?.integerOnly && !Number.isInteger(value)) {
|
||||
result.integerOnly = true;
|
||||
}
|
||||
if (isPopulatedObject(result)) {
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
|
@ -5,7 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { isPopulatedObject } from '@kbn/ml-is-populated-object';
|
||||
import { ALLOWED_DATA_UNITS } from '../constants/validation';
|
||||
import { parseInterval } from './parse_interval';
|
||||
|
||||
|
@ -100,40 +99,3 @@ export function timeIntervalInputValidator() {
|
|||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
export interface NumberValidationResult {
|
||||
min: boolean;
|
||||
max: boolean;
|
||||
integerOnly: boolean;
|
||||
}
|
||||
|
||||
export function numberValidator(conditions?: {
|
||||
min?: number;
|
||||
max?: number;
|
||||
integerOnly?: boolean;
|
||||
}) {
|
||||
if (
|
||||
conditions?.min !== undefined &&
|
||||
conditions.max !== undefined &&
|
||||
conditions.min > conditions.max
|
||||
) {
|
||||
throw new Error('Invalid validator conditions');
|
||||
}
|
||||
|
||||
return (value: number): NumberValidationResult | null => {
|
||||
const result = {} as NumberValidationResult;
|
||||
if (conditions?.min !== undefined && value < conditions.min) {
|
||||
result.min = true;
|
||||
}
|
||||
if (conditions?.max !== undefined && value > conditions.max) {
|
||||
result.max = true;
|
||||
}
|
||||
if (!!conditions?.integerOnly && !Number.isInteger(value)) {
|
||||
result.integerOnly = true;
|
||||
}
|
||||
if (isPopulatedObject(result)) {
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { numberValidator, timeIntervalInputValidator } from '../../common/util/validators';
|
||||
|
||||
import { numberValidator } from '@kbn/ml-agg-utils';
|
||||
import { timeIntervalInputValidator } from '../../common/util/validators';
|
||||
export const validateLookbackInterval = timeIntervalInputValidator();
|
||||
export const validateTopNBucket = numberValidator({ min: 1 });
|
||||
|
|
|
@ -32,12 +32,9 @@ import { toMountPoint, wrapWithTheme } from '@kbn/kibana-react-plugin/public';
|
|||
import type { Observable } from 'rxjs';
|
||||
import type { CoreTheme, OverlayStart } from '@kbn/core/public';
|
||||
import { css } from '@emotion/react';
|
||||
import { numberValidator } from '@kbn/ml-agg-utils';
|
||||
import { isCloud } from '../../services/ml_server_info';
|
||||
import {
|
||||
composeValidators,
|
||||
numberValidator,
|
||||
requiredValidator,
|
||||
} from '../../../../common/util/validators';
|
||||
import { composeValidators, requiredValidator } from '../../../../common/util/validators';
|
||||
|
||||
interface StartDeploymentSetup {
|
||||
config: ThreadingParams;
|
||||
|
|
|
@ -70,6 +70,8 @@ export const settingsSchema = schema.object({
|
|||
max_page_search_size: schema.maybe(schema.nullable(schema.number())),
|
||||
// The default value is null, which disables throttling.
|
||||
docs_per_second: schema.maybe(schema.nullable(schema.number())),
|
||||
// Optional value that takes precedence over cluster's setting.
|
||||
num_failure_retries: schema.maybe(schema.nullable(schema.number())),
|
||||
});
|
||||
|
||||
export const sourceSchema = schema.object({
|
||||
|
|
|
@ -25,6 +25,7 @@ export type TransformBaseConfig = PutTransformsRequestSchema & {
|
|||
version?: string;
|
||||
alerting_rules?: TransformHealthAlertRule[];
|
||||
_meta?: Record<string, unknown>;
|
||||
authorization?: object;
|
||||
};
|
||||
|
||||
export interface PivotConfigDefinition {
|
||||
|
|
|
@ -311,6 +311,7 @@ describe('Transform: Common', () => {
|
|||
transformFrequency: '10m',
|
||||
transformSettingsMaxPageSearchSize: 100,
|
||||
transformSettingsDocsPerSecond: 400,
|
||||
transformSettingsNumFailureRetries: 5,
|
||||
destinationIndex: 'the-destination-index',
|
||||
destinationIngestPipeline: 'the-destination-ingest-pipeline',
|
||||
touched: true,
|
||||
|
@ -334,6 +335,7 @@ describe('Transform: Common', () => {
|
|||
settings: {
|
||||
max_page_search_size: 100,
|
||||
docs_per_second: 400,
|
||||
num_failure_retries: 5,
|
||||
},
|
||||
source: {
|
||||
index: ['the-data-view-title'],
|
||||
|
|
|
@ -206,6 +206,9 @@ export const getCreateTransformSettingsRequestBody = (
|
|||
DEFAULT_TRANSFORM_SETTINGS_DOCS_PER_SECOND
|
||||
? { docs_per_second: transformDetailsState.transformSettingsDocsPerSecond }
|
||||
: {}),
|
||||
...(typeof transformDetailsState.transformSettingsNumFailureRetries === 'number'
|
||||
? { num_failure_retries: transformDetailsState.transformSettingsNumFailureRetries }
|
||||
: {}),
|
||||
};
|
||||
return Object.keys(settings).length > 0 ? { settings } : {};
|
||||
};
|
||||
|
|
|
@ -33,6 +33,7 @@ export interface StepDetailsExposedState {
|
|||
transformFrequency: string;
|
||||
transformSettingsMaxPageSearchSize: number;
|
||||
transformSettingsDocsPerSecond: number | null;
|
||||
transformSettingsNumFailureRetries?: number;
|
||||
valid: boolean;
|
||||
dataViewTimeField?: string | undefined;
|
||||
_meta?: Record<string, unknown>;
|
||||
|
@ -52,6 +53,7 @@ export function getDefaultStepDetailsState(): StepDetailsExposedState {
|
|||
transformFrequency: DEFAULT_TRANSFORM_FREQUENCY,
|
||||
transformSettingsMaxPageSearchSize: DEFAULT_TRANSFORM_SETTINGS_MAX_PAGE_SEARCH_SIZE,
|
||||
transformSettingsDocsPerSecond: DEFAULT_TRANSFORM_SETTINGS_DOCS_PER_SECOND,
|
||||
transformSettingsNumFailureRetries: undefined,
|
||||
destinationIndex: '',
|
||||
destinationIngestPipeline: '',
|
||||
touched: false,
|
||||
|
@ -107,6 +109,9 @@ export function applyTransformConfigToDetailsState(
|
|||
} else {
|
||||
state.transformSettingsDocsPerSecond = null;
|
||||
}
|
||||
if (typeof transformConfig.settings?.num_failure_retries === 'number') {
|
||||
state.transformSettingsNumFailureRetries = transformConfig.settings.num_failure_retries;
|
||||
}
|
||||
}
|
||||
|
||||
if (transformConfig._meta) {
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
import { KBN_FIELD_TYPES } from '@kbn/data-plugin/common';
|
||||
import { toMountPoint } from '@kbn/kibana-react-plugin/public';
|
||||
|
||||
import { integerRangeMinus1To100Validator } from '../../../transform_management/components/edit_transform_flyout/use_edit_transform_flyout';
|
||||
import {
|
||||
isEsIndices,
|
||||
isEsIngestPipelines,
|
||||
|
@ -304,6 +305,14 @@ export const StepDetailsForm: FC<StepDetailsFormProps> = React.memo(
|
|||
transformSettingsMaxPageSearchSize
|
||||
);
|
||||
|
||||
const [transformSettingsNumFailureRetries, setTransformSettingsNumFailureRetries] = useState<
|
||||
string | number | undefined
|
||||
>(defaults.transformSettingsNumFailureRetries);
|
||||
const isTransformSettingsNumFailureRetriesValid =
|
||||
transformSettingsNumFailureRetries === undefined ||
|
||||
transformSettingsNumFailureRetries === '-' ||
|
||||
integerRangeMinus1To100Validator(transformSettingsNumFailureRetries).length === 0;
|
||||
|
||||
const valid =
|
||||
!transformIdEmpty &&
|
||||
transformIdValid &&
|
||||
|
@ -336,6 +345,13 @@ export const StepDetailsForm: FC<StepDetailsFormProps> = React.memo(
|
|||
transformFrequency,
|
||||
transformSettingsMaxPageSearchSize,
|
||||
transformSettingsDocsPerSecond,
|
||||
transformSettingsNumFailureRetries:
|
||||
transformSettingsNumFailureRetries === undefined ||
|
||||
transformSettingsNumFailureRetries === ''
|
||||
? undefined
|
||||
: typeof transformSettingsNumFailureRetries === 'number'
|
||||
? transformSettingsNumFailureRetries
|
||||
: parseInt(transformSettingsNumFailureRetries, 10),
|
||||
destinationIndex,
|
||||
destinationIngestPipeline,
|
||||
touched: true,
|
||||
|
@ -357,6 +373,7 @@ export const StepDetailsForm: FC<StepDetailsFormProps> = React.memo(
|
|||
transformDescription,
|
||||
transformFrequency,
|
||||
transformSettingsMaxPageSearchSize,
|
||||
transformSettingsNumFailureRetries,
|
||||
destinationIndex,
|
||||
destinationIngestPipeline,
|
||||
valid,
|
||||
|
@ -840,6 +857,58 @@ export const StepDetailsForm: FC<StepDetailsFormProps> = React.memo(
|
|||
data-test-subj="transformMaxPageSearchSizeInput"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
data-test-subj="transformNumFailureRetriesFormRow"
|
||||
label={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.transformNumFailureRetriesLabel',
|
||||
{
|
||||
defaultMessage: 'Number of failure retries',
|
||||
}
|
||||
)}
|
||||
isInvalid={!isTransformSettingsNumFailureRetriesValid}
|
||||
error={
|
||||
!isTransformSettingsNumFailureRetriesValid && [
|
||||
i18n.translate('xpack.transform.stepDetailsForm.NumFailureRetriesError', {
|
||||
defaultMessage:
|
||||
'Number of retries needs to be between 0 and 100, or -1 for infinite retries.',
|
||||
}),
|
||||
]
|
||||
}
|
||||
helpText={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.transformNumRetriesHelpText',
|
||||
{
|
||||
defaultMessage:
|
||||
'The number of retries on a recoverable failure before the transform task is marked as failed. Set it to -1 for infinite retries.',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<EuiFieldText
|
||||
value={
|
||||
transformSettingsNumFailureRetries ||
|
||||
(transformSettingsNumFailureRetries !== undefined &&
|
||||
transformSettingsNumFailureRetries >= -1)
|
||||
? transformSettingsNumFailureRetries.toString()
|
||||
: ''
|
||||
}
|
||||
onChange={(e) => {
|
||||
if (e.target.value === '') {
|
||||
setTransformSettingsNumFailureRetries(undefined);
|
||||
return;
|
||||
}
|
||||
setTransformSettingsNumFailureRetries(
|
||||
e.target.value === '-' ? '-' : parseInt(e.target.value, 10)
|
||||
);
|
||||
}}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.transform.stepDetailsForm.numFailureRetriesAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Choose a maximum number of retries.',
|
||||
}
|
||||
)}
|
||||
isInvalid={!isTransformSettingsNumFailureRetriesValid}
|
||||
data-test-subj="transformNumFailureRetriesInput"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiAccordion>
|
||||
</EuiForm>
|
||||
</div>
|
||||
|
|
|
@ -25,6 +25,7 @@ export const StepDetailsSummary: FC<StepDetailsExposedState> = React.memo((props
|
|||
transformDescription,
|
||||
transformFrequency,
|
||||
transformSettingsMaxPageSearchSize,
|
||||
transformSettingsNumFailureRetries,
|
||||
destinationIndex,
|
||||
destinationIngestPipeline,
|
||||
touched,
|
||||
|
@ -153,6 +154,16 @@ export const StepDetailsSummary: FC<StepDetailsExposedState> = React.memo((props
|
|||
>
|
||||
<span>{transformSettingsMaxPageSearchSize}</span>
|
||||
</EuiFormRow>
|
||||
{typeof transformSettingsNumFailureRetries === 'number' ? (
|
||||
<EuiFormRow
|
||||
data-test-subj={'transformWizardAdvancedSettingsNumFailureRetriesLabel'}
|
||||
label={i18n.translate('xpack.transform.stepDetailsSummary.numFailureRetriesLabel', {
|
||||
defaultMessage: 'Number of retries',
|
||||
})}
|
||||
>
|
||||
<span>{transformSettingsNumFailureRetries}</span>
|
||||
</EuiFormRow>
|
||||
) : null}
|
||||
</EuiAccordion>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -323,7 +323,7 @@ export const EditTransformFlyoutForm: FC<EditTransformFlyoutFormProps> = ({
|
|||
dataTestSubj="transformEditFlyoutDocsPerSecondInput"
|
||||
errorMessages={formFields.docsPerSecond.errorMessages}
|
||||
helpText={i18n.translate(
|
||||
'xpack.transform.transformList.editFlyoutFormDocsPerSecondHelptext',
|
||||
'xpack.transform.transformList.editFlyoutFormDocsPerSecondHelpText',
|
||||
{
|
||||
defaultMessage:
|
||||
'To enable throttling, set a limit of documents to input per second.',
|
||||
|
@ -343,7 +343,7 @@ export const EditTransformFlyoutForm: FC<EditTransformFlyoutFormProps> = ({
|
|||
dataTestSubj="transformEditFlyoutMaxPageSearchSizeInput"
|
||||
errorMessages={formFields.maxPageSearchSize.errorMessages}
|
||||
helpText={i18n.translate(
|
||||
'xpack.transform.transformList.editFlyoutFormMaxPageSearchSizeHelptext',
|
||||
'xpack.transform.transformList.editFlyoutFormMaxPageSearchSizeHelpText',
|
||||
{
|
||||
defaultMessage:
|
||||
'The initial page size to use for the composite aggregation for each checkpoint.',
|
||||
|
@ -365,6 +365,22 @@ export const EditTransformFlyoutForm: FC<EditTransformFlyoutFormProps> = ({
|
|||
}
|
||||
)}
|
||||
/>
|
||||
<EditTransformFlyoutFormTextInput
|
||||
dataTestSubj="transformEditFlyoutNumFailureRetriesInput"
|
||||
errorMessages={formFields.numFailureRetries.errorMessages}
|
||||
helpText={i18n.translate(
|
||||
'xpack.transform.transformList.editFlyoutFormNumFailureRetriesHelpText',
|
||||
{
|
||||
defaultMessage:
|
||||
'The number of retries on a recoverable failure before the transform task is marked as failed. Set it to -1 for infinite retries.',
|
||||
}
|
||||
)}
|
||||
label={i18n.translate('xpack.transform.transformList.numFailureRetriesLabel', {
|
||||
defaultMessage: 'Number of failure retries',
|
||||
})}
|
||||
onChange={(value) => dispatch({ field: 'numFailureRetries', value })}
|
||||
value={formFields.numFailureRetries.value}
|
||||
/>
|
||||
</div>
|
||||
</EuiAccordion>
|
||||
</EuiForm>
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { isEqual } from 'lodash';
|
||||
import { merge } from 'lodash';
|
||||
import { numberValidator } from '@kbn/ml-agg-utils';
|
||||
|
||||
import { useReducer } from 'react';
|
||||
|
||||
|
@ -44,7 +45,8 @@ type EditTransformFormFields =
|
|||
| 'docsPerSecond'
|
||||
| 'maxPageSearchSize'
|
||||
| 'retentionPolicyField'
|
||||
| 'retentionPolicyMaxAge';
|
||||
| 'retentionPolicyMaxAge'
|
||||
| 'numFailureRetries';
|
||||
|
||||
type EditTransformFlyoutFieldsState = Record<EditTransformFormFields, FormField>;
|
||||
|
||||
|
@ -107,16 +109,30 @@ type Validator = (value: any, isOptional?: boolean) => string[];
|
|||
// We do this so we have fine grained control over field validation and the option to
|
||||
// cast to special values like `null` for disabling `docs_per_second`.
|
||||
const numberAboveZeroNotValidErrorMessage = i18n.translate(
|
||||
'xpack.transform.transformList.editFlyoutFormNumberNotValidErrorMessage',
|
||||
'xpack.transform.transformList.editFlyoutFormNumberAboveZeroNotValidErrorMessage',
|
||||
{
|
||||
defaultMessage: 'Value needs to be an integer above zero.',
|
||||
}
|
||||
);
|
||||
|
||||
const numberRangeMinus1To100NotValidErrorMessage = i18n.translate(
|
||||
'xpack.transform.transformList.editFlyoutFormNumberGreaterThanOrEqualToNegativeOneNotValidErrorMessage',
|
||||
{
|
||||
defaultMessage: 'Number of retries needs to be between 0 and 100, or -1 for infinite retries.',
|
||||
}
|
||||
);
|
||||
|
||||
export const integerAboveZeroValidator: Validator = (value) =>
|
||||
!isNaN(value) && Number.isInteger(+value) && +value > 0 && !(value + '').includes('.')
|
||||
!(value + '').includes('.') && numberValidator({ min: 1, integerOnly: true })(+value) === null
|
||||
? []
|
||||
: [numberAboveZeroNotValidErrorMessage];
|
||||
|
||||
export const integerRangeMinus1To100Validator: Validator = (value) =>
|
||||
!(value + '').includes('.') &&
|
||||
numberValidator({ min: -1, max: 100, integerOnly: true })(+value) === null
|
||||
? []
|
||||
: [numberRangeMinus1To100NotValidErrorMessage];
|
||||
|
||||
const numberRange10To10000NotValidErrorMessage = i18n.translate(
|
||||
'xpack.transform.transformList.editFlyoutFormNumberRange10To10000NotValidErrorMessage',
|
||||
{
|
||||
|
@ -124,7 +140,8 @@ const numberRange10To10000NotValidErrorMessage = i18n.translate(
|
|||
}
|
||||
);
|
||||
export const integerRange10To10000Validator: Validator = (value) =>
|
||||
integerAboveZeroValidator(value).length === 0 && +value >= 10 && +value <= 10000
|
||||
!(value + '').includes('.') &&
|
||||
numberValidator({ min: 10, max: 100001, integerOnly: true })(+value) === null
|
||||
? []
|
||||
: [numberRange10To10000NotValidErrorMessage];
|
||||
|
||||
|
@ -214,6 +231,7 @@ const validate = {
|
|||
string: stringValidator,
|
||||
frequency: frequencyValidator,
|
||||
integerAboveZero: integerAboveZeroValidator,
|
||||
integerRangeMinus1To100: integerRangeMinus1To100Validator,
|
||||
integerRange10To10000: integerRange10To10000Validator,
|
||||
retentionPolicyMaxAge: retentionPolicyMaxAgeValidator,
|
||||
} as const;
|
||||
|
@ -407,6 +425,18 @@ export const getDefaultState = (config: TransformConfigUnion): EditTransformFlyo
|
|||
valueParser: (v) => +v,
|
||||
}
|
||||
),
|
||||
numFailureRetries: initializeField(
|
||||
'numFailureRetries',
|
||||
'settings.num_failure_retries',
|
||||
config,
|
||||
{
|
||||
defaultValue: undefined,
|
||||
isNullable: true,
|
||||
isOptional: true,
|
||||
validator: 'integerRangeMinus1To100',
|
||||
valueParser: (v) => +v,
|
||||
}
|
||||
),
|
||||
|
||||
// retention_policy.*
|
||||
retentionPolicyField: initializeField(
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import React, { FC, useMemo } from 'react';
|
||||
|
||||
import { EuiButtonEmpty, EuiTabbedContent } from '@elastic/eui';
|
||||
import { Optional } from '@kbn/utility-types';
|
||||
|
@ -13,6 +13,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { stringHash } from '@kbn/ml-string-hash';
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
import { isDefined } from '../../../../../../common/types/common';
|
||||
import { TransformListRow } from '../../../../common';
|
||||
import { useAppDependencies } from '../../../../app_dependencies';
|
||||
import { ExpandedRowDetailsPane, SectionConfig, SectionItem } from './expanded_row_details_pane';
|
||||
|
@ -70,37 +71,52 @@ export const ExpandedRow: FC<Props> = ({ item, onAlertEdit }) => {
|
|||
position: 'right',
|
||||
};
|
||||
|
||||
const configItems: Item[] = [
|
||||
{
|
||||
title: 'transform_id',
|
||||
description: item.id,
|
||||
},
|
||||
{
|
||||
title: 'transform_version',
|
||||
description: item.config.version,
|
||||
},
|
||||
{
|
||||
title: 'description',
|
||||
description: item.config.description ?? '',
|
||||
},
|
||||
{
|
||||
title: 'create_time',
|
||||
description:
|
||||
formatHumanReadableDateTimeSeconds(moment(item.config.create_time).unix() * 1000) ?? '',
|
||||
},
|
||||
{
|
||||
title: 'source_index',
|
||||
description: Array.isArray(item.config.source.index)
|
||||
? item.config.source.index[0]
|
||||
: item.config.source.index,
|
||||
},
|
||||
{
|
||||
title: 'destination_index',
|
||||
description: Array.isArray(item.config.dest.index)
|
||||
? item.config.dest.index[0]
|
||||
: item.config.dest.index,
|
||||
},
|
||||
];
|
||||
const configItems = useMemo(() => {
|
||||
const configs: Item[] = [
|
||||
{
|
||||
title: 'transform_id',
|
||||
description: item.id,
|
||||
},
|
||||
{
|
||||
title: 'transform_version',
|
||||
description: item.config.version,
|
||||
},
|
||||
{
|
||||
title: 'description',
|
||||
description: item.config.description ?? '',
|
||||
},
|
||||
{
|
||||
title: 'create_time',
|
||||
description:
|
||||
formatHumanReadableDateTimeSeconds(moment(item.config.create_time).unix() * 1000) ?? '',
|
||||
},
|
||||
{
|
||||
title: 'source_index',
|
||||
description: Array.isArray(item.config.source.index)
|
||||
? item.config.source.index[0]
|
||||
: item.config.source.index,
|
||||
},
|
||||
{
|
||||
title: 'destination_index',
|
||||
description: Array.isArray(item.config.dest.index)
|
||||
? item.config.dest.index[0]
|
||||
: item.config.dest.index,
|
||||
},
|
||||
{
|
||||
title: 'authorization',
|
||||
description: item.config.authorization ? JSON.stringify(item.config.authorization) : '',
|
||||
},
|
||||
];
|
||||
if (isDefined(item.config.settings?.num_failure_retries)) {
|
||||
configs.push({
|
||||
title: 'num_failure_retries',
|
||||
description: item.config.settings?.num_failure_retries ?? '',
|
||||
});
|
||||
}
|
||||
return configs;
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [item?.config]);
|
||||
|
||||
const general: SectionConfig = {
|
||||
title: 'General',
|
||||
|
|
|
@ -30342,16 +30342,16 @@
|
|||
"xpack.transform.transformList.editFlyoutFormDestinationButtonContent": "Configuration de destination",
|
||||
"xpack.transform.transformList.editFlyoutFormDestinationIndexLabel": "Index de destination",
|
||||
"xpack.transform.transformList.editFlyoutFormDestinationIngestPipelineLabel": "Pipeline d'ingestion",
|
||||
"xpack.transform.transformList.editFlyoutFormDocsPerSecondHelptext": "Pour activer la régulation, définissez une limite de documents à saisir par seconde.",
|
||||
"xpack.transform.transformList.editFlyoutFormDocsPerSecondHelpText": "Pour activer la régulation, définissez une limite de documents à saisir par seconde.",
|
||||
"xpack.transform.transformList.editFlyoutFormDocsPerSecondLabel": "Documents par seconde",
|
||||
"xpack.transform.transformList.editFlyoutFormFrequencyHelpText": "Intervalle entre les vérifications de modifications dans les index source lorsque la transformation est exécutée en continu. Détermine également l'intervalle des nouvelles tentatives en cas d'échecs temporaires lorsque la transformation effectue une recherche ou une indexation. La valeur minimale est de 1 s et la valeur maximale de 1 h.",
|
||||
"xpack.transform.transformList.editFlyoutFormFrequencyLabel": "Fréquence",
|
||||
"xpack.transform.transformList.editFlyoutFormFrequencyNotValidErrorMessage": "La valeur de fréquence n'est pas valide.",
|
||||
"xpack.transform.transformList.editFlyoutFormFrequencyPlaceholderText": "Par défaut : {defaultValue}",
|
||||
"xpack.transform.transformList.editFlyoutFormMaxPageSearchSizeHelptext": "Définit la taille de pages initiale à utiliser pour l'agrégation imbriquée de chaque point de contrôle.",
|
||||
"xpack.transform.transformList.editFlyoutFormMaxPageSearchSizeHelpText": "Définit la taille de pages initiale à utiliser pour l'agrégation imbriquée de chaque point de contrôle.",
|
||||
"xpack.transform.transformList.editFlyoutFormMaxPageSearchSizeLabel": "Taille maximale de recherche de pages",
|
||||
"xpack.transform.transformList.editFlyoutFormMaxPageSearchSizePlaceholderText": "Par défaut : {defaultValue}",
|
||||
"xpack.transform.transformList.editFlyoutFormNumberNotValidErrorMessage": "La valeur doit être un entier supérieur à zéro.",
|
||||
"xpack.transform.transformList.editFlyoutFormNumberAboveZeroNotValidErrorMessage": "La valeur doit être un entier supérieur à zéro.",
|
||||
"xpack.transform.transformList.editFlyoutFormNumberRange10To10000NotValidErrorMessage": "La valeur doit être un entier compris entre 10 et 10 000.",
|
||||
"xpack.transform.transformList.editFlyoutFormRequiredErrorMessage": "Champs requis.",
|
||||
"xpack.transform.transformList.editFlyoutFormRetentionMaxAgeFieldLabel": "Âge maximal",
|
||||
|
|
|
@ -30326,16 +30326,16 @@
|
|||
"xpack.transform.transformList.editFlyoutFormDestinationButtonContent": "ディスティネーション構成",
|
||||
"xpack.transform.transformList.editFlyoutFormDestinationIndexLabel": "デスティネーションインデックス",
|
||||
"xpack.transform.transformList.editFlyoutFormDestinationIngestPipelineLabel": "インジェストパイプライン",
|
||||
"xpack.transform.transformList.editFlyoutFormDocsPerSecondHelptext": "スロットリングを有効にするには、毎秒入力するドキュメントの上限を設定します。",
|
||||
"xpack.transform.transformList.editFlyoutFormDocsPerSecondHelpText": "スロットリングを有効にするには、毎秒入力するドキュメントの上限を設定します。",
|
||||
"xpack.transform.transformList.editFlyoutFormDocsPerSecondLabel": "毎秒あたりのドキュメント",
|
||||
"xpack.transform.transformList.editFlyoutFormFrequencyHelpText": "変換が連続実行されているときにソースインデックスで変更を確認する間の間隔。また、変換が検索またはインデックス中に一時障害が発生した場合に、再試行する間隔も決定します。最小値は1秒で、最大値は1時間です。",
|
||||
"xpack.transform.transformList.editFlyoutFormFrequencyLabel": "頻度",
|
||||
"xpack.transform.transformList.editFlyoutFormFrequencyNotValidErrorMessage": "頻度値が無効です。",
|
||||
"xpack.transform.transformList.editFlyoutFormFrequencyPlaceholderText": "デフォルト:{defaultValue}",
|
||||
"xpack.transform.transformList.editFlyoutFormMaxPageSearchSizeHelptext": "各チェックポイントの複合集計で使用する、初期ページサイズを定義します。",
|
||||
"xpack.transform.transformList.editFlyoutFormMaxPageSearchSizeHelpText": "各チェックポイントの複合集計で使用する、初期ページサイズを定義します。",
|
||||
"xpack.transform.transformList.editFlyoutFormMaxPageSearchSizeLabel": "最大ページ検索サイズ",
|
||||
"xpack.transform.transformList.editFlyoutFormMaxPageSearchSizePlaceholderText": "デフォルト:{defaultValue}",
|
||||
"xpack.transform.transformList.editFlyoutFormNumberNotValidErrorMessage": "値は1以上の整数でなければなりません。",
|
||||
"xpack.transform.transformList.editFlyoutFormNumberAboveZeroNotValidErrorMessage": "値は1以上の整数でなければなりません。",
|
||||
"xpack.transform.transformList.editFlyoutFormNumberRange10To10000NotValidErrorMessage": "値は10~10000の範囲の整数でなければなりません。",
|
||||
"xpack.transform.transformList.editFlyoutFormRequiredErrorMessage": "必須フィールド。",
|
||||
"xpack.transform.transformList.editFlyoutFormRetentionMaxAgeFieldLabel": "最大年齢",
|
||||
|
|
|
@ -30354,16 +30354,16 @@
|
|||
"xpack.transform.transformList.editFlyoutFormDestinationButtonContent": "目标配置",
|
||||
"xpack.transform.transformList.editFlyoutFormDestinationIndexLabel": "目标索引",
|
||||
"xpack.transform.transformList.editFlyoutFormDestinationIngestPipelineLabel": "采集管道",
|
||||
"xpack.transform.transformList.editFlyoutFormDocsPerSecondHelptext": "要启用节流,请设置每秒要输入的文档限值。",
|
||||
"xpack.transform.transformList.editFlyoutFormDocsPerSecondHelpText": "要启用节流,请设置每秒要输入的文档限值。",
|
||||
"xpack.transform.transformList.editFlyoutFormDocsPerSecondLabel": "每秒文档数",
|
||||
"xpack.transform.transformList.editFlyoutFormFrequencyHelpText": "在转换不间断地执行时检查源索引更改的时间间隔。还确定在转换搜索或索引时发生暂时失败时的重试时间间隔。最小值为 1 秒,最大值为 1 小时。",
|
||||
"xpack.transform.transformList.editFlyoutFormFrequencyLabel": "频率",
|
||||
"xpack.transform.transformList.editFlyoutFormFrequencyNotValidErrorMessage": "频率值无效。",
|
||||
"xpack.transform.transformList.editFlyoutFormFrequencyPlaceholderText": "默认值:{defaultValue}",
|
||||
"xpack.transform.transformList.editFlyoutFormMaxPageSearchSizeHelptext": "定义用于每个检查点的组合聚合的初始页面大小。",
|
||||
"xpack.transform.transformList.editFlyoutFormMaxPageSearchSizeHelpText": "定义用于每个检查点的组合聚合的初始页面大小。",
|
||||
"xpack.transform.transformList.editFlyoutFormMaxPageSearchSizeLabel": "最大页面搜索大小",
|
||||
"xpack.transform.transformList.editFlyoutFormMaxPageSearchSizePlaceholderText": "默认值:{defaultValue}",
|
||||
"xpack.transform.transformList.editFlyoutFormNumberNotValidErrorMessage": "值必须是大于零的整数。",
|
||||
"xpack.transform.transformList.editFlyoutFormNumberAboveZeroNotValidErrorMessage": "值必须是大于零的整数。",
|
||||
"xpack.transform.transformList.editFlyoutFormNumberRange10To10000NotValidErrorMessage": "值必须是介于 10 到 10000 之间的整数。",
|
||||
"xpack.transform.transformList.editFlyoutFormRequiredErrorMessage": "必填字段。",
|
||||
"xpack.transform.transformList.editFlyoutFormRetentionMaxAgeFieldLabel": "最大存在时间",
|
||||
|
|
|
@ -23,6 +23,11 @@ interface TestData {
|
|||
expected: any;
|
||||
}
|
||||
|
||||
function getNumFailureRetriesStr(value: number | null | undefined) {
|
||||
if (value === null || value === undefined) return '';
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
function getTransformConfig(): TransformPivotConfig {
|
||||
const date = Date.now();
|
||||
return {
|
||||
|
@ -38,6 +43,7 @@ function getTransformConfig(): TransformPivotConfig {
|
|||
retention_policy: { time: { field: 'order_date', max_age: '1d' } },
|
||||
settings: {
|
||||
max_page_search_size: 250,
|
||||
num_failure_retries: 0,
|
||||
},
|
||||
dest: { index: `user-ec_2_${date}` },
|
||||
};
|
||||
|
@ -76,6 +82,7 @@ function getTransformConfigWithRuntimeMappings(): TransformPivotConfig {
|
|||
retention_policy: { time: { field: 'order_date', max_age: '3d' } },
|
||||
settings: {
|
||||
max_page_search_size: 250,
|
||||
num_failure_retries: 5,
|
||||
},
|
||||
dest: { index: `user-ec_2_${date}` },
|
||||
};
|
||||
|
@ -161,6 +168,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
retentionPolicySwitchEnabled: true,
|
||||
retentionPolicyField: 'order_date',
|
||||
retentionPolicyMaxAge: '1d',
|
||||
numFailureRetries: getNumFailureRetriesStr(
|
||||
transformConfigWithPivot.settings?.num_failure_retries
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -193,6 +203,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
retentionPolicySwitchEnabled: true,
|
||||
retentionPolicyField: 'order_date',
|
||||
retentionPolicyMaxAge: '3d',
|
||||
numFailureRetries: getNumFailureRetriesStr(
|
||||
transformConfigWithRuntimeMapping.settings?.num_failure_retries
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -366,10 +379,23 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await transform.wizard.assertTransformMaxPageSearchSizeValue(
|
||||
testData.originalConfig.settings!.max_page_search_size!
|
||||
);
|
||||
if (testData.expected.numFailureRetries !== undefined) {
|
||||
await transform.wizard.assertNumFailureRetriesValue(
|
||||
testData.expected.numFailureRetries
|
||||
);
|
||||
}
|
||||
|
||||
await transform.testExecution.logTestStep('should load the create step');
|
||||
await transform.wizard.advanceToCreateStep();
|
||||
|
||||
if (testData.expected.numFailureRetries !== undefined) {
|
||||
await transform.testExecution.logTestStep('displays the summary details');
|
||||
await transform.wizard.openTransformAdvancedSettingsSummaryAccordion();
|
||||
await transform.wizard.assertTransformNumFailureRetriesSummaryValue(
|
||||
testData.expected.numFailureRetries
|
||||
);
|
||||
}
|
||||
|
||||
await transform.testExecution.logTestStep('should display the create and start button');
|
||||
await transform.wizard.assertCreateAndStartButtonExists();
|
||||
await transform.wizard.assertCreateAndStartButtonEnabled(true);
|
||||
|
|
|
@ -35,6 +35,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce');
|
||||
});
|
||||
|
||||
const DEFAULT_NUM_FAILURE_RETRIES = '5';
|
||||
const testDataList: Array<PivotTransformTestData | LatestTransformTestData> = [
|
||||
{
|
||||
type: 'pivot',
|
||||
|
@ -92,6 +93,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
return `user-${this.transformId}`;
|
||||
},
|
||||
discoverAdjustSuperDatePicker: true,
|
||||
numFailureRetries: '7',
|
||||
expected: {
|
||||
pivotAdvancedEditorValueArr: ['{', ' "group_by": {', ' "category": {'],
|
||||
pivotAdvancedEditorValue: {
|
||||
|
@ -250,6 +252,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
},
|
||||
],
|
||||
discoverQueryHits: '7,270',
|
||||
numFailureRetries: '7',
|
||||
},
|
||||
} as PivotTransformTestData,
|
||||
{
|
||||
|
@ -288,6 +291,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
return `user-${this.transformId}`;
|
||||
},
|
||||
discoverAdjustSuperDatePicker: false,
|
||||
numFailureRetries: '-1',
|
||||
expected: {
|
||||
pivotAdvancedEditorValueArr: ['{', ' "group_by": {', ' "geoip.country_iso_code": {'],
|
||||
pivotAdvancedEditorValue: {
|
||||
|
@ -335,6 +339,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
rows: 5,
|
||||
},
|
||||
discoverQueryHits: '10',
|
||||
numFailureRetries: '-1',
|
||||
},
|
||||
} as PivotTransformTestData,
|
||||
{
|
||||
|
@ -360,6 +365,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
return `user-${this.transformId}`;
|
||||
},
|
||||
discoverAdjustSuperDatePicker: false,
|
||||
numFailureRetries: '0',
|
||||
expected: {
|
||||
pivotAdvancedEditorValueArr: ['{', ' "group_by": {', ' "customer_gender": {'],
|
||||
pivotAdvancedEditorValue: {
|
||||
|
@ -393,6 +399,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
rows: 5,
|
||||
},
|
||||
discoverQueryHits: '2',
|
||||
numFailureRetries: '0',
|
||||
},
|
||||
} as PivotTransformTestData,
|
||||
{
|
||||
|
@ -418,6 +425,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
},
|
||||
destinationDataViewTimeField: 'order_date',
|
||||
discoverAdjustSuperDatePicker: true,
|
||||
numFailureRetries: '101',
|
||||
expected: {
|
||||
latestPreview: {
|
||||
column: 0,
|
||||
|
@ -443,6 +451,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
],
|
||||
},
|
||||
discoverQueryHits: '10',
|
||||
numFailureRetries: 'error',
|
||||
},
|
||||
} as LatestTransformTestData,
|
||||
];
|
||||
|
@ -602,9 +611,40 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await transform.wizard.assertContinuousModeSwitchExists();
|
||||
await transform.wizard.assertContinuousModeSwitchCheckState(false);
|
||||
|
||||
await transform.testExecution.logTestStep(
|
||||
'should display the advanced settings and show pre-filled configuration'
|
||||
);
|
||||
await transform.wizard.openTransformAdvancedSettingsAccordion();
|
||||
if (
|
||||
testData.numFailureRetries !== undefined &&
|
||||
testData.expected.numFailureRetries !== undefined
|
||||
) {
|
||||
await transform.wizard.assertNumFailureRetriesValue('');
|
||||
await transform.wizard.setTransformNumFailureRetriesValue(
|
||||
testData.numFailureRetries.toString(),
|
||||
testData.expected.numFailureRetries
|
||||
);
|
||||
// If num failure input is expected to give an error, sets it back to a valid
|
||||
// so that we can continue creating the transform
|
||||
if (testData.expected.numFailureRetries === 'error') {
|
||||
await transform.wizard.setTransformNumFailureRetriesValue(
|
||||
DEFAULT_NUM_FAILURE_RETRIES,
|
||||
DEFAULT_NUM_FAILURE_RETRIES
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await transform.testExecution.logTestStep('loads the create step');
|
||||
await transform.wizard.advanceToCreateStep();
|
||||
|
||||
await transform.testExecution.logTestStep('displays the summary details');
|
||||
await transform.wizard.openTransformAdvancedSettingsSummaryAccordion();
|
||||
await transform.wizard.assertTransformNumFailureRetriesSummaryValue(
|
||||
testData.expected.numFailureRetries === 'error'
|
||||
? DEFAULT_NUM_FAILURE_RETRIES
|
||||
: testData.expected.numFailureRetries
|
||||
);
|
||||
|
||||
await transform.testExecution.logTestStep('displays the create and start button');
|
||||
await transform.wizard.assertCreateAndStartButtonExists();
|
||||
await transform.wizard.assertCreateAndStartButtonEnabled(true);
|
||||
|
|
|
@ -61,6 +61,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
resetRetentionPolicy: false,
|
||||
transformRetentionPolicyField: 'order_date',
|
||||
transformRetentionPolicyMaxAge: '1d',
|
||||
numFailureRetries: '0',
|
||||
expected: {
|
||||
messageText: 'updated transform.',
|
||||
retentionPolicy: {
|
||||
|
@ -82,6 +83,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
transformDocsPerSecond: '1000',
|
||||
transformFrequency: '10m',
|
||||
resetRetentionPolicy: true,
|
||||
numFailureRetries: '7',
|
||||
expected: {
|
||||
messageText: 'updated transform.',
|
||||
retentionPolicy: {
|
||||
|
@ -145,6 +147,17 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
testData.transformDocsPerSecond
|
||||
);
|
||||
|
||||
await transform.testExecution.logTestStep(
|
||||
'should update the transform number of failure retries'
|
||||
);
|
||||
await transform.editFlyout.openTransformEditAccordionAdvancedSettings();
|
||||
await transform.editFlyout.assertTransformEditFlyoutInputExists('NumFailureRetries');
|
||||
await transform.editFlyout.assertTransformEditFlyoutInputValue('NumFailureRetries', '');
|
||||
await transform.editFlyout.setTransformEditFlyoutInputValue(
|
||||
'NumFailureRetries',
|
||||
testData.numFailureRetries
|
||||
);
|
||||
|
||||
await transform.testExecution.logTestStep('should update the transform frequency');
|
||||
await transform.editFlyout.assertTransformEditFlyoutInputExists('Frequency');
|
||||
await transform.editFlyout.assertTransformEditFlyoutInputValue(
|
||||
|
|
|
@ -67,6 +67,7 @@ export interface BaseTransformTestData {
|
|||
destinationIndex: string;
|
||||
destinationDataViewTimeField?: string;
|
||||
discoverAdjustSuperDatePicker: boolean;
|
||||
numFailureRetries?: string;
|
||||
}
|
||||
|
||||
export interface PivotTransformTestData extends BaseTransformTestData {
|
||||
|
|
|
@ -24,6 +24,7 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi
|
|||
const testSubjects = getService('testSubjects');
|
||||
const comboBox = getService('comboBox');
|
||||
const retry = getService('retry');
|
||||
const find = getService('find');
|
||||
const ml = getService('ml');
|
||||
const PageObjects = getPageObjects(['discover', 'timePicker', 'unifiedSearch']);
|
||||
|
||||
|
@ -798,6 +799,72 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi
|
|||
);
|
||||
},
|
||||
|
||||
async assertTransformNumFailureRetriesInputExists() {
|
||||
await testSubjects.existOrFail('transformNumFailureRetriesInput');
|
||||
expect(await testSubjects.isDisplayed('transformNumFailureRetriesInput')).to.eql(
|
||||
true,
|
||||
`Expected 'Number of retries failure' input to be displayed`
|
||||
);
|
||||
},
|
||||
|
||||
async assertNumFailureRetriesValue(expectedValue: string) {
|
||||
await this.assertTransformNumFailureRetriesInputExists();
|
||||
const actualValue = await testSubjects.getAttribute(
|
||||
'transformNumFailureRetriesInput',
|
||||
'value'
|
||||
);
|
||||
expect(actualValue).to.eql(
|
||||
expectedValue,
|
||||
`Transform num failure retries text should be '${expectedValue}' (got '${actualValue}')`
|
||||
);
|
||||
},
|
||||
|
||||
async assertTransformNumFailureRetriesErrorMessageExists(expected: boolean) {
|
||||
const row = await testSubjects.find('transformNumFailureRetriesFormRow');
|
||||
const errorElements = await row.findAllByClassName('euiFormErrorText');
|
||||
|
||||
if (expected) {
|
||||
expect(errorElements.length).greaterThan(
|
||||
0,
|
||||
'Expected Transform num failure retries to display error message'
|
||||
);
|
||||
} else {
|
||||
expect(errorElements.length).eql(
|
||||
0,
|
||||
'Expected Transform num failure retries to not display error message'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
async setTransformNumFailureRetriesValue(value: string, expectedResult: 'error' | string) {
|
||||
await retry.tryForTime(5000, async () => {
|
||||
await testSubjects.setValue('transformNumFailureRetriesInput', value);
|
||||
if (expectedResult !== 'error') {
|
||||
await this.assertTransformNumFailureRetriesErrorMessageExists(false);
|
||||
await this.assertNumFailureRetriesValue(expectedResult);
|
||||
} else {
|
||||
await this.assertTransformNumFailureRetriesErrorMessageExists(true);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async assertTransformNumFailureRetriesSummaryValue(expectedValue: string) {
|
||||
await retry.tryForTime(5000, async () => {
|
||||
await testSubjects.existOrFail('transformWizardAdvancedSettingsNumFailureRetriesLabel');
|
||||
const row = await testSubjects.find(
|
||||
'transformWizardAdvancedSettingsNumFailureRetriesLabel'
|
||||
);
|
||||
const actualValue = await (
|
||||
await row.findByClassName('euiFormRow__fieldWrapper')
|
||||
).getVisibleText();
|
||||
|
||||
expect(actualValue).to.eql(
|
||||
expectedValue,
|
||||
`Transform num failure retries summary value should be '${expectedValue}' (got '${actualValue}')`
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
async assertTransformMaxPageSearchSizeInputExists() {
|
||||
await testSubjects.existOrFail('transformMaxPageSearchSizeInput');
|
||||
expect(await testSubjects.isDisplayed('transformMaxPageSearchSizeInput')).to.eql(
|
||||
|
@ -817,6 +884,22 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi
|
|||
);
|
||||
},
|
||||
|
||||
async assertAccordionAdvancedSettingsSummaryAccordionExists() {
|
||||
await testSubjects.existOrFail('transformWizardAccordionAdvancedSettingsSummary');
|
||||
},
|
||||
|
||||
// for now we expect this to be used only for opening the accordion
|
||||
async openTransformAdvancedSettingsSummaryAccordion() {
|
||||
await this.assertAccordionAdvancedSettingsSummaryAccordionExists();
|
||||
await find.clickByCssSelector(
|
||||
'[aria-controls="transformWizardAccordionAdvancedSettingsSummary"]'
|
||||
);
|
||||
|
||||
await testSubjects.existOrFail('transformWizardAdvancedSettingsFrequencyLabel');
|
||||
await testSubjects.existOrFail('transformWizardAdvancedSettingsMaxPageSearchSizeLabel');
|
||||
await testSubjects.existOrFail('transformWizardAdvancedSettingsNumFailureRetriesLabel');
|
||||
},
|
||||
|
||||
async assertCreateAndStartButtonExists() {
|
||||
await testSubjects.existOrFail('transformWizardCreateAndStartButton');
|
||||
expect(await testSubjects.isDisplayed('transformWizardCreateAndStartButton')).to.eql(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue