mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Index Management] Add index mode field in index template form (#199521)
Closes https://github.com/elastic/kibana/issues/198620 ## Summary This PR adds a field for index mode setting in the Logistics step in Index Template form. https://github.com/user-attachments/assets/ee38bdec-66ff-468d-a55e-abf5354c3da2 **How to test:** 1. Go to Index Management -> Index Templates and start creating an index template 2. Verify that the index mode is only enabled if the data stream toggle is on. 3. Verify that typing the `logs-*-*` index pattern sets the index mode to "LogsDB": <img width="1401" alt="Screenshot 2024-11-13 at 13 00 10" src="https://github.com/user-attachments/assets/d1825d08-5039-4c43-80a8-653233e0b677"> 4. Go to the Settings step and verify that the index mode callout is displayed correctly. 5. Go to Review step and verify that Index mode is displayed correctly in both the summary and the preview request. 6. Save the template and verify that the template details tab correctly displays the index mode. <img width="1565" alt="Screenshot 2024-11-13 at 17 22 54" src="https://github.com/user-attachments/assets/2055501b-32c9-463c-b61d-541b9687b459"> <img width="1565" alt="Screenshot 2024-11-13 at 17 22 31" src="https://github.com/user-attachments/assets/21c9cf9e-5858-4403-9106-57ed8ccf3639"> ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
This commit is contained in:
parent
4d1cb0b586
commit
16127fcc8f
26 changed files with 403 additions and 56 deletions
|
@ -52,6 +52,15 @@ jest.mock('@elastic/eui', () => {
|
|||
}}
|
||||
/>
|
||||
),
|
||||
EuiSuperSelect: (props: any) => (
|
||||
<input
|
||||
data-test-subj={props['data-test-subj'] || 'mockSuperSelect'}
|
||||
value={props.valueOfSelected}
|
||||
onChange={(e) => {
|
||||
props.onChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -301,6 +310,15 @@ describe('<TemplateCreate />', () => {
|
|||
expect(find('stepTitle').text()).toEqual('Index settings (optional)');
|
||||
});
|
||||
|
||||
it('should display a warning callout displaying the selected index mode', async () => {
|
||||
const { exists, find } = testBed;
|
||||
|
||||
expect(exists('indexModeCallout')).toBe(true);
|
||||
expect(find('indexModeCallout').text()).toContain(
|
||||
'The index.mode setting has been set to Standard within template Logistics.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should not allow invalid json', async () => {
|
||||
const { form, actions } = testBed;
|
||||
|
||||
|
@ -426,6 +444,53 @@ describe('<TemplateCreate />', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('logistics (step 1)', () => {
|
||||
beforeEach(async () => {
|
||||
await act(async () => {
|
||||
testBed = await setup(httpSetup);
|
||||
});
|
||||
testBed.component.update();
|
||||
});
|
||||
|
||||
it('setting index pattern to logs-*-* should set the index mode to logsdb', async () => {
|
||||
const { component, actions } = testBed;
|
||||
// Logistics
|
||||
await actions.completeStepOne({ name: 'my_logs_template', indexPatterns: ['logs-*-*'] });
|
||||
// Component templates
|
||||
await actions.completeStepTwo();
|
||||
// Index settings
|
||||
await actions.completeStepThree('{}');
|
||||
// Mappings
|
||||
await actions.completeStepFour();
|
||||
// Aliases
|
||||
await actions.completeStepFive();
|
||||
|
||||
await act(async () => {
|
||||
actions.clickNextButton();
|
||||
});
|
||||
component.update();
|
||||
|
||||
expect(httpSetup.post).toHaveBeenLastCalledWith(
|
||||
`${API_BASE_PATH}/index_templates`,
|
||||
expect.objectContaining({
|
||||
body: JSON.stringify({
|
||||
name: 'my_logs_template',
|
||||
indexPatterns: ['logs-*-*'],
|
||||
allowAutoCreate: 'NO_OVERWRITE',
|
||||
indexMode: 'logsdb',
|
||||
dataStream: {},
|
||||
_kbnMeta: {
|
||||
type: 'default',
|
||||
hasDatastream: false,
|
||||
isLegacy: false,
|
||||
},
|
||||
template: {},
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('review (step 6)', () => {
|
||||
beforeEach(async () => {
|
||||
await act(async () => {
|
||||
|
@ -529,6 +594,7 @@ describe('<TemplateCreate />', () => {
|
|||
name: TEMPLATE_NAME,
|
||||
indexPatterns: DEFAULT_INDEX_PATTERNS,
|
||||
allowAutoCreate: 'TRUE',
|
||||
indexMode: 'time_series',
|
||||
});
|
||||
// Component templates
|
||||
await actions.completeStepTwo('test_component_template_1');
|
||||
|
@ -557,6 +623,7 @@ describe('<TemplateCreate />', () => {
|
|||
name: TEMPLATE_NAME,
|
||||
indexPatterns: DEFAULT_INDEX_PATTERNS,
|
||||
allowAutoCreate: 'TRUE',
|
||||
indexMode: 'time_series',
|
||||
dataStream: {},
|
||||
_kbnMeta: {
|
||||
type: 'default',
|
||||
|
|
|
@ -169,6 +169,7 @@ describe('<TemplateEdit />', () => {
|
|||
indexPatterns: ['myPattern*'],
|
||||
version: 1,
|
||||
allowAutoCreate: 'NO_OVERWRITE',
|
||||
indexMode: 'standard',
|
||||
dataStream: {
|
||||
hidden: true,
|
||||
anyUnknownKey: 'should_be_kept',
|
||||
|
|
|
@ -148,6 +148,7 @@ export const formSetup = async (initTestBed: SetupFunc<TestSubjects>) => {
|
|||
enableDataStream,
|
||||
lifecycle,
|
||||
allowAutoCreate,
|
||||
indexMode,
|
||||
}: Partial<TemplateDeserialized> & { enableDataStream?: boolean } = {}) => {
|
||||
const { component, form, find } = testBed;
|
||||
|
||||
|
@ -204,6 +205,10 @@ export const formSetup = async (initTestBed: SetupFunc<TestSubjects>) => {
|
|||
radioOption.simulate('change', { target: { checked: true } });
|
||||
component.update();
|
||||
}
|
||||
|
||||
if (indexMode) {
|
||||
form.setSelectValue('indexModeField', indexMode);
|
||||
}
|
||||
});
|
||||
component.update();
|
||||
|
||||
|
@ -356,6 +361,7 @@ export type TestSubjects =
|
|||
| 'mappingsEditorFieldEdit'
|
||||
| 'mockCodeEditor'
|
||||
| 'mockComboBox'
|
||||
| 'mockSuperSelect'
|
||||
| 'nameField'
|
||||
| 'nameField.input'
|
||||
| 'nameParameterInput'
|
||||
|
@ -364,6 +370,8 @@ export type TestSubjects =
|
|||
| 'orderField.input'
|
||||
| 'priorityField.input'
|
||||
| 'dataStreamField.input'
|
||||
| 'indexModeField'
|
||||
| 'indexModeCallout'
|
||||
| 'dataRetentionToggle.input'
|
||||
| 'allowAutoCreateField.input'
|
||||
| 'pageTitle'
|
||||
|
|
|
@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n';
|
|||
export { BASE_PATH } from './base_path';
|
||||
export { API_BASE_PATH, INTERNAL_API_BASE_PATH } from './api_base_path';
|
||||
export { INVALID_INDEX_PATTERN_CHARS, INVALID_TEMPLATE_NAME_CHARS } from './invalid_characters';
|
||||
export * from './index_modes';
|
||||
export * from './index_statuses';
|
||||
|
||||
// Since each index can have a max length or 255 characters and the max length of
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export const STANDARD_INDEX_MODE = 'standard';
|
||||
export const LOGSDB_INDEX_MODE = 'logsdb';
|
||||
export const TIME_SERIES_MODE = 'time_series';
|
|
@ -6,7 +6,8 @@
|
|||
*/
|
||||
|
||||
import { deserializeTemplate, serializeTemplate } from './template_serialization';
|
||||
import { TemplateDeserialized, TemplateSerialized } from '../types';
|
||||
import { TemplateDeserialized, TemplateSerialized, IndexMode } from '../types';
|
||||
import { STANDARD_INDEX_MODE, LOGSDB_INDEX_MODE, TIME_SERIES_MODE } from '../constants';
|
||||
|
||||
const defaultSerializedTemplate: TemplateSerialized = {
|
||||
template: {},
|
||||
|
@ -17,6 +18,7 @@ const defaultSerializedTemplate: TemplateSerialized = {
|
|||
const defaultDeserializedTemplate: TemplateDeserialized = {
|
||||
name: 'my_template',
|
||||
indexPatterns: ['test'],
|
||||
indexMode: STANDARD_INDEX_MODE,
|
||||
_kbnMeta: {
|
||||
type: 'default',
|
||||
hasDatastream: true,
|
||||
|
@ -26,12 +28,13 @@ const defaultDeserializedTemplate: TemplateDeserialized = {
|
|||
|
||||
const allowAutoCreateRadioOptions = ['NO_OVERWRITE', 'TRUE', 'FALSE'];
|
||||
const allowAutoCreateSerializedValues = [undefined, true, false];
|
||||
const indexModeValues = [STANDARD_INDEX_MODE, LOGSDB_INDEX_MODE, TIME_SERIES_MODE, undefined];
|
||||
|
||||
describe('Template serialization', () => {
|
||||
describe('serialization of allow_auto_create parameter', () => {
|
||||
describe('deserializeTemplate()', () => {
|
||||
allowAutoCreateSerializedValues.forEach((value, index) => {
|
||||
test(`correctly deserializes ${value} value`, () => {
|
||||
test(`correctly deserializes ${value} allow_auto_create value`, () => {
|
||||
expect(
|
||||
deserializeTemplate({
|
||||
...defaultSerializedTemplate,
|
||||
|
@ -41,11 +44,29 @@ describe('Template serialization', () => {
|
|||
).toHaveProperty('allowAutoCreate', allowAutoCreateRadioOptions[index]);
|
||||
});
|
||||
});
|
||||
|
||||
indexModeValues.forEach((value) => {
|
||||
test(`correctly deserializes ${value} index mode settings value`, () => {
|
||||
expect(
|
||||
deserializeTemplate({
|
||||
...defaultSerializedTemplate,
|
||||
name: 'my_template',
|
||||
template: {
|
||||
settings: {
|
||||
index: {
|
||||
mode: value,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
).toHaveProperty('indexMode', value ?? STANDARD_INDEX_MODE);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('serializeTemplate()', () => {
|
||||
allowAutoCreateRadioOptions.forEach((option, index) => {
|
||||
test(`correctly serializes ${option} radio option`, () => {
|
||||
test(`correctly serializes ${option} allowAutoCreate radio option`, () => {
|
||||
expect(
|
||||
serializeTemplate({
|
||||
...defaultDeserializedTemplate,
|
||||
|
@ -54,6 +75,18 @@ describe('Template serialization', () => {
|
|||
).toHaveProperty('allow_auto_create', allowAutoCreateSerializedValues[index]);
|
||||
});
|
||||
});
|
||||
|
||||
// Only use the first three values (omit undefined)
|
||||
indexModeValues.slice(0, 3).forEach((value) => {
|
||||
test(`correctly serializes ${value} indexMode option`, () => {
|
||||
expect(
|
||||
serializeTemplate({
|
||||
...defaultDeserializedTemplate,
|
||||
indexMode: value as IndexMode,
|
||||
})
|
||||
).toHaveProperty('template.settings.index.mode', value);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,9 +11,15 @@ import {
|
|||
TemplateSerialized,
|
||||
TemplateListItem,
|
||||
TemplateType,
|
||||
IndexMode,
|
||||
} from '../types';
|
||||
import { deserializeESLifecycle } from './data_stream_utils';
|
||||
import { allowAutoCreateRadioValues, allowAutoCreateRadioIds } from '../constants';
|
||||
import {
|
||||
allowAutoCreateRadioValues,
|
||||
allowAutoCreateRadioIds,
|
||||
STANDARD_INDEX_MODE,
|
||||
LOGSDB_INDEX_MODE,
|
||||
} from '../constants';
|
||||
|
||||
const hasEntries = (data: object = {}) => Object.entries(data).length > 0;
|
||||
|
||||
|
@ -26,6 +32,7 @@ export function serializeTemplate(templateDeserialized: TemplateDeserialized): T
|
|||
composedOf,
|
||||
ignoreMissingComponentTemplates,
|
||||
dataStream,
|
||||
indexMode,
|
||||
_meta,
|
||||
allowAutoCreate,
|
||||
deprecated,
|
||||
|
@ -34,7 +41,16 @@ export function serializeTemplate(templateDeserialized: TemplateDeserialized): T
|
|||
return {
|
||||
version,
|
||||
priority,
|
||||
template,
|
||||
template: {
|
||||
...template,
|
||||
settings: {
|
||||
...template?.settings,
|
||||
index: {
|
||||
...template?.settings?.index,
|
||||
mode: indexMode,
|
||||
},
|
||||
},
|
||||
},
|
||||
index_patterns: indexPatterns,
|
||||
data_stream: dataStream,
|
||||
composed_of: composedOf,
|
||||
|
@ -75,6 +91,11 @@ export function deserializeTemplate(
|
|||
|
||||
const ilmPolicyName = settings?.index?.lifecycle?.name;
|
||||
|
||||
const indexMode = (settings?.index?.mode ??
|
||||
(indexPatterns.some((pattern) => pattern === 'logs-*-*')
|
||||
? LOGSDB_INDEX_MODE
|
||||
: STANDARD_INDEX_MODE)) as IndexMode;
|
||||
|
||||
const deserializedTemplate: TemplateDeserialized = {
|
||||
name,
|
||||
version,
|
||||
|
@ -82,6 +103,7 @@ export function deserializeTemplate(
|
|||
...(template.lifecycle ? { lifecycle: deserializeESLifecycle(template.lifecycle) } : {}),
|
||||
indexPatterns: indexPatterns.sort(),
|
||||
template,
|
||||
indexMode,
|
||||
ilmPolicy: ilmPolicyName ? { name: ilmPolicyName } : undefined,
|
||||
composedOf: composedOf ?? [],
|
||||
ignoreMissingComponentTemplates: ignoreMissingComponentTemplates ?? [],
|
||||
|
|
|
@ -19,6 +19,7 @@ export type {
|
|||
DataStream,
|
||||
DataStreamIndex,
|
||||
DataRetention,
|
||||
IndexMode,
|
||||
} from './data_streams';
|
||||
|
||||
export * from './component_templates';
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { DataRetention, DataStream } from './data_streams';
|
||||
import { DataRetention, DataStream, IndexMode } from './data_streams';
|
||||
import { IndexSettings } from './indices';
|
||||
import { Aliases } from './aliases';
|
||||
import { Mappings } from './mappings';
|
||||
|
@ -51,6 +51,7 @@ export interface TemplateDeserialized {
|
|||
priority?: number; // Composable template only
|
||||
allowAutoCreate: string;
|
||||
order?: number; // Legacy template only
|
||||
indexMode: IndexMode;
|
||||
ilmPolicy?: {
|
||||
name: string;
|
||||
};
|
||||
|
|
|
@ -16,6 +16,8 @@ import {
|
|||
EuiFormRow,
|
||||
EuiText,
|
||||
EuiCode,
|
||||
EuiCallOut,
|
||||
EuiLink,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { CodeEditor } from '@kbn/code-editor';
|
||||
|
@ -23,15 +25,19 @@ import { CodeEditor } from '@kbn/code-editor';
|
|||
import { Forms } from '../../../../../shared_imports';
|
||||
import { useJsonStep } from './use_json_step';
|
||||
import { documentationService } from '../../../mappings_editor/shared_imports';
|
||||
import { indexModeLabels } from '../../../../lib/index_mode_labels';
|
||||
import { IndexMode } from '../../../../../../common/types';
|
||||
|
||||
interface Props {
|
||||
onChange: (content: Forms.Content) => void;
|
||||
esDocsBase: string;
|
||||
defaultValue?: { [key: string]: any };
|
||||
indexMode?: IndexMode;
|
||||
}
|
||||
|
||||
export const StepSettings: React.FunctionComponent<Props> = React.memo(
|
||||
({ defaultValue = {}, onChange, esDocsBase }) => {
|
||||
({ defaultValue = {}, onChange, esDocsBase, indexMode }) => {
|
||||
const { navigateToStep } = Forms.useFormWizardContext();
|
||||
const { jsonContent, setJsonContent, error } = useJsonStep({
|
||||
defaultValue,
|
||||
onChange,
|
||||
|
@ -80,6 +86,47 @@ export const StepSettings: React.FunctionComponent<Props> = React.memo(
|
|||
|
||||
<EuiSpacer size="l" />
|
||||
|
||||
{indexMode && (
|
||||
<>
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.idxMgmt.formWizard.stepSettings.indexModeCallout.title"
|
||||
defaultMessage="The {settingName} setting has been set to {indexMode} within template {logisticsLink}. Any changes to {settingName} set on this page will be overwritten by the Logistics selection."
|
||||
values={{
|
||||
settingName: (
|
||||
<EuiCode>
|
||||
{i18n.translate(
|
||||
'xpack.idxMgmt.formWizard.stepSettings.indexModeCallout.indexModeSettingLabel',
|
||||
{
|
||||
defaultMessage: 'index.mode',
|
||||
}
|
||||
)}
|
||||
</EuiCode>
|
||||
),
|
||||
indexMode: indexModeLabels[indexMode],
|
||||
logisticsLink: (
|
||||
<EuiLink onClick={() => navigateToStep(0)}>
|
||||
{i18n.translate(
|
||||
'xpack.idxMgmt.formWizard.stepSettings.indexModeCallout.logisticsLinkLabel',
|
||||
{
|
||||
defaultMessage: 'Logistics',
|
||||
}
|
||||
)}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
}
|
||||
color="warning"
|
||||
iconType="warning"
|
||||
data-test-subj="indexModeCallout"
|
||||
/>
|
||||
|
||||
<EuiSpacer size="l" />
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Settings code editor */}
|
||||
<EuiFormRow
|
||||
label={
|
||||
|
|
|
@ -8,19 +8,36 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Forms } from '../../../../../shared_imports';
|
||||
import { TemplateDeserialized } from '../../../../../../common';
|
||||
import { WizardContent } from '../../../template_form/template_form';
|
||||
import { CommonWizardSteps } from './types';
|
||||
import { StepSettings } from './step_settings';
|
||||
|
||||
interface Props {
|
||||
esDocsBase: string;
|
||||
getTemplateData?: (wizardContent: WizardContent) => TemplateDeserialized;
|
||||
}
|
||||
|
||||
export const StepSettingsContainer = React.memo(({ esDocsBase }: Props) => {
|
||||
export const StepSettingsContainer = React.memo(({ esDocsBase, getTemplateData }: Props) => {
|
||||
const { defaultValue, updateContent } = Forms.useContent<CommonWizardSteps, 'settings'>(
|
||||
'settings'
|
||||
);
|
||||
const { getData } = Forms.useMultiContentContext<WizardContent>();
|
||||
|
||||
let indexMode;
|
||||
if (getTemplateData) {
|
||||
const wizardContent = getData();
|
||||
// Build the current template object, providing the wizard content data
|
||||
const template = getTemplateData(wizardContent);
|
||||
indexMode = template.indexMode;
|
||||
}
|
||||
|
||||
return (
|
||||
<StepSettings defaultValue={defaultValue} onChange={updateContent} esDocsBase={esDocsBase} />
|
||||
<StepSettings
|
||||
defaultValue={defaultValue}
|
||||
onChange={updateContent}
|
||||
esDocsBase={esDocsBase}
|
||||
indexMode={indexMode}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import React, { useEffect, useCallback, Fragment } from 'react';
|
||||
import {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
|
@ -14,6 +14,7 @@ import {
|
|||
EuiSpacer,
|
||||
EuiLink,
|
||||
EuiCode,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
@ -34,7 +35,13 @@ import { UnitField, timeUnits } from '../../shared';
|
|||
import { DataRetention } from '../../../../../common';
|
||||
import { documentationService } from '../../../services/documentation';
|
||||
import { schemas, nameConfig, nameConfigWithoutValidations } from '../template_form_schemas';
|
||||
import { allowAutoCreateRadios } from '../../../../../common/constants';
|
||||
import {
|
||||
allowAutoCreateRadios,
|
||||
STANDARD_INDEX_MODE,
|
||||
TIME_SERIES_MODE,
|
||||
LOGSDB_INDEX_MODE,
|
||||
} from '../../../../../common/constants';
|
||||
import { indexModeLabels, indexModeDescriptions } from '../../../lib/index_mode_labels';
|
||||
|
||||
// Create or Form components with partial props that are common to all instances
|
||||
const UseField = getUseField({ component: Field });
|
||||
|
@ -91,6 +98,54 @@ function getFieldsMeta(esDocsBase: string) {
|
|||
),
|
||||
testSubject: 'dataStreamField',
|
||||
},
|
||||
indexMode: {
|
||||
title: i18n.translate('xpack.idxMgmt.templateForm.stepLogistics.indexModeTitle', {
|
||||
defaultMessage: 'Data stream index mode',
|
||||
}),
|
||||
description: i18n.translate('xpack.idxMgmt.templateForm.stepLogistics.indexModeDescription', {
|
||||
defaultMessage:
|
||||
'The index.mode setting is used to control settings applied in specific domains like ingestions of time series data or logs.',
|
||||
}),
|
||||
options: [
|
||||
{
|
||||
value: STANDARD_INDEX_MODE,
|
||||
inputDisplay: indexModeLabels[STANDARD_INDEX_MODE],
|
||||
dropdownDisplay: (
|
||||
<Fragment>
|
||||
<strong>{indexModeLabels[STANDARD_INDEX_MODE]}</strong>
|
||||
<EuiText size="s" color="subdued">
|
||||
<p>{indexModeDescriptions[STANDARD_INDEX_MODE]}</p>
|
||||
</EuiText>
|
||||
</Fragment>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: TIME_SERIES_MODE,
|
||||
inputDisplay: indexModeLabels[TIME_SERIES_MODE],
|
||||
dropdownDisplay: (
|
||||
<Fragment>
|
||||
<strong>{indexModeLabels[TIME_SERIES_MODE]}</strong>
|
||||
<EuiText size="s" color="subdued">
|
||||
<p>{indexModeDescriptions[TIME_SERIES_MODE]}</p>
|
||||
</EuiText>
|
||||
</Fragment>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: LOGSDB_INDEX_MODE,
|
||||
inputDisplay: indexModeLabels[LOGSDB_INDEX_MODE],
|
||||
dropdownDisplay: (
|
||||
<Fragment>
|
||||
<strong>{indexModeLabels[LOGSDB_INDEX_MODE]}</strong>
|
||||
<EuiText size="s" color="subdued">
|
||||
<p>{indexModeDescriptions[LOGSDB_INDEX_MODE]}</p>
|
||||
</EuiText>
|
||||
</Fragment>
|
||||
),
|
||||
},
|
||||
],
|
||||
testSubject: 'indexModeField',
|
||||
},
|
||||
order: {
|
||||
title: i18n.translate('xpack.idxMgmt.templateForm.stepLogistics.orderTitle', {
|
||||
defaultMessage: 'Merge order',
|
||||
|
@ -198,21 +253,37 @@ export const StepLogistics: React.FunctionComponent<Props> = React.memo(
|
|||
isValid: isFormValid,
|
||||
getErrors: getFormErrors,
|
||||
getFormData,
|
||||
setFieldValue,
|
||||
} = form;
|
||||
|
||||
const [{ addMeta, doCreateDataStream, lifecycle }] = useFormData<{
|
||||
addMeta: boolean;
|
||||
lifecycle: DataRetention;
|
||||
doCreateDataStream: boolean;
|
||||
}>({
|
||||
form,
|
||||
watch: [
|
||||
'addMeta',
|
||||
'lifecycle.enabled',
|
||||
'lifecycle.infiniteDataRetention',
|
||||
'doCreateDataStream',
|
||||
],
|
||||
});
|
||||
const [{ addMeta, doCreateDataStream, lifecycle, indexPatterns: indexPatternsField }] =
|
||||
useFormData<{
|
||||
addMeta: boolean;
|
||||
lifecycle: DataRetention;
|
||||
doCreateDataStream: boolean;
|
||||
indexPatterns: string[];
|
||||
}>({
|
||||
form,
|
||||
watch: [
|
||||
'addMeta',
|
||||
'lifecycle.enabled',
|
||||
'lifecycle.infiniteDataRetention',
|
||||
'doCreateDataStream',
|
||||
'indexPatterns',
|
||||
],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
indexPatternsField &&
|
||||
indexPatternsField.length === 1 &&
|
||||
indexPatternsField[0] === 'logs-*-*' &&
|
||||
// Only set index mode if index pattern was changed
|
||||
defaultValue.indexPatterns !== indexPatternsField
|
||||
) {
|
||||
setFieldValue('indexMode', LOGSDB_INDEX_MODE);
|
||||
}
|
||||
}, [defaultValue.indexPatterns, indexPatternsField, setFieldValue]);
|
||||
|
||||
/**
|
||||
* When the consumer call validate() on this step, we submit the form so it enters the "isSubmitted" state
|
||||
|
@ -234,6 +305,7 @@ export const StepLogistics: React.FunctionComponent<Props> = React.memo(
|
|||
name,
|
||||
indexPatterns,
|
||||
createDataStream,
|
||||
indexMode,
|
||||
order,
|
||||
priority,
|
||||
version,
|
||||
|
@ -312,6 +384,21 @@ export const StepLogistics: React.FunctionComponent<Props> = React.memo(
|
|||
</FormRow>
|
||||
)}
|
||||
|
||||
{doCreateDataStream && (
|
||||
<FormRow title={indexMode.title} description={indexMode.description}>
|
||||
<UseField
|
||||
path="indexMode"
|
||||
componentProps={{
|
||||
euiFieldProps: {
|
||||
hasDividers: true,
|
||||
'data-test-subj': indexMode.testSubject,
|
||||
options: indexMode.options,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormRow>
|
||||
)}
|
||||
|
||||
{/*
|
||||
Since data stream and data retention are settings that are only allowed for non legacy,
|
||||
we only need to check if data stream is set to true to show the data retention.
|
||||
|
|
|
@ -22,7 +22,7 @@ import {
|
|||
EuiCodeBlock,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { getIndexModeLabel } from '../../../lib/index_mode_labels';
|
||||
import { indexModeLabels } from '../../../lib/index_mode_labels';
|
||||
import { allowAutoCreateRadioIds } from '../../../../../common/constants';
|
||||
import { serializers } from '../../../../shared_imports';
|
||||
|
||||
|
@ -89,6 +89,7 @@ export const StepReview: React.FunctionComponent<Props> = React.memo(
|
|||
const {
|
||||
name,
|
||||
indexPatterns,
|
||||
indexMode,
|
||||
version,
|
||||
order,
|
||||
template: indexTemplate,
|
||||
|
@ -277,9 +278,7 @@ export const StepReview: React.FunctionComponent<Props> = React.memo(
|
|||
/>
|
||||
</EuiDescriptionListTitle>
|
||||
<EuiDescriptionListDescription data-test-subj="indexModeValue">
|
||||
{getIndexModeLabel(
|
||||
serializedSettings?.['index.mode'] ?? serializedSettings?.index?.mode
|
||||
)}
|
||||
{indexModeLabels[indexMode]}
|
||||
</EuiDescriptionListDescription>
|
||||
|
||||
{/* Mappings */}
|
||||
|
|
|
@ -11,7 +11,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
import { EuiSpacer, EuiButton, EuiPageHeader } from '@elastic/eui';
|
||||
import { ScopedHistory } from '@kbn/core/public';
|
||||
|
||||
import { allowAutoCreateRadioIds } from '../../../../common/constants';
|
||||
import { allowAutoCreateRadioIds, STANDARD_INDEX_MODE } from '../../../../common/constants';
|
||||
import { TemplateDeserialized } from '../../../../common';
|
||||
import { serializers, Forms, GlobalFlyout } from '../../../shared_imports';
|
||||
import {
|
||||
|
@ -118,6 +118,7 @@ export const TemplateForm = ({
|
|||
name: '',
|
||||
indexPatterns: [],
|
||||
dataStream: {},
|
||||
indexMode: STANDARD_INDEX_MODE,
|
||||
template: {},
|
||||
_kbnMeta: {
|
||||
type: 'default',
|
||||
|
@ -341,7 +342,10 @@ export const TemplateForm = ({
|
|||
)}
|
||||
|
||||
<FormWizardStep id={wizardSections.settings.id} label={wizardSections.settings.label}>
|
||||
<StepSettingsContainer esDocsBase={documentationService.getEsDocsBase()} />
|
||||
<StepSettingsContainer
|
||||
esDocsBase={documentationService.getEsDocsBase()}
|
||||
getTemplateData={buildTemplateObject(indexTemplate)}
|
||||
/>
|
||||
</FormWizardStep>
|
||||
|
||||
<FormWizardStep id={wizardSections.mappings.id} label={wizardSections.mappings.label}>
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
allowAutoCreateRadioIds,
|
||||
INVALID_INDEX_PATTERN_CHARS,
|
||||
INVALID_TEMPLATE_NAME_CHARS,
|
||||
STANDARD_INDEX_MODE,
|
||||
} from '../../../../common/constants';
|
||||
|
||||
const {
|
||||
|
@ -150,6 +151,13 @@ export const schemas: Record<string, FormSchema> = {
|
|||
}),
|
||||
defaultValue: false,
|
||||
},
|
||||
indexMode: {
|
||||
type: FIELD_TYPES.SUPER_SELECT,
|
||||
defaultValue: STANDARD_INDEX_MODE,
|
||||
label: i18n.translate('xpack.idxMgmt.templateForm.stepLogistics.fieldIndexModeLabel', {
|
||||
defaultMessage: 'Index mode',
|
||||
}),
|
||||
},
|
||||
order: {
|
||||
type: FIELD_TYPES.NUMBER,
|
||||
label: i18n.translate('xpack.idxMgmt.templateForm.stepLogistics.fieldOrderLabel', {
|
||||
|
|
|
@ -6,24 +6,43 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
STANDARD_INDEX_MODE,
|
||||
LOGSDB_INDEX_MODE,
|
||||
TIME_SERIES_MODE,
|
||||
} from '../../../common/constants';
|
||||
|
||||
export const getIndexModeLabel = (mode?: string | null) => {
|
||||
switch (mode) {
|
||||
case 'standard':
|
||||
case null:
|
||||
case undefined:
|
||||
return i18n.translate('xpack.idxMgmt.indexModeLabels.standardModeLabel', {
|
||||
defaultMessage: 'Standard',
|
||||
});
|
||||
case 'logsdb':
|
||||
return i18n.translate('xpack.idxMgmt.indexModeLabels.logsdbModeLabel', {
|
||||
defaultMessage: 'LogsDB',
|
||||
});
|
||||
case 'time_series':
|
||||
return i18n.translate('xpack.idxMgmt.indexModeLabels.tsdbModeLabel', {
|
||||
defaultMessage: 'Time series',
|
||||
});
|
||||
default:
|
||||
return mode;
|
||||
}
|
||||
export const indexModeLabels = {
|
||||
[STANDARD_INDEX_MODE]: i18n.translate('xpack.idxMgmt.indexModeLabels.standardIndexModeLabel', {
|
||||
defaultMessage: 'Standard',
|
||||
}),
|
||||
[LOGSDB_INDEX_MODE]: i18n.translate('xpack.idxMgmt.indexModeLabels.logsdbIndexModeLabel', {
|
||||
defaultMessage: 'LogsDB',
|
||||
}),
|
||||
[TIME_SERIES_MODE]: i18n.translate('xpack.idxMgmt.indexModeLabels.timeSeriesIndexModeLabel', {
|
||||
defaultMessage: 'Time series',
|
||||
}),
|
||||
};
|
||||
|
||||
export const indexModeDescriptions = {
|
||||
[STANDARD_INDEX_MODE]: i18n.translate(
|
||||
'xpack.idxMgmt.indexModeDescriptions.standardIndexModeDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'Standard indexing with default settings, for data other than logs or metrics',
|
||||
}
|
||||
),
|
||||
[LOGSDB_INDEX_MODE]: i18n.translate(
|
||||
'xpack.idxMgmt.indexModeDescriptions.logsdbIndexModeDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'Optimized for storing logs data, with reduced storage and default settings that help reduce the chance of logs being rejected by Elasticsearch',
|
||||
}
|
||||
),
|
||||
[TIME_SERIES_MODE]: i18n.translate(
|
||||
'xpack.idxMgmt.indexModeDescriptions.timeSeriesIndexModeDescription',
|
||||
{
|
||||
defaultMessage: 'Optimized for metrics data with reduced storage',
|
||||
}
|
||||
),
|
||||
};
|
||||
|
|
|
@ -34,7 +34,7 @@ import {
|
|||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { getIndexModeLabel } from '../../../../lib/index_mode_labels';
|
||||
import { indexModeLabels } from '../../../../lib/index_mode_labels';
|
||||
import { DiscoverLink } from '../../../../lib/discover_link';
|
||||
import { getLifecycleValue } from '../../../../lib/data_streams';
|
||||
import { SectionLoading, reactRouterNavigate } from '../../../../../shared_imports';
|
||||
|
@ -355,7 +355,7 @@ export const DataStreamDetailPanel: React.FunctionComponent<Props> = ({
|
|||
defaultMessage:
|
||||
"The index mode applied to the data stream's backing indices, as defined in its associated index template.",
|
||||
}),
|
||||
content: getIndexModeLabel(indexMode),
|
||||
content: indexModeLabels[indexMode],
|
||||
dataTestSubj: 'indexModeDetail',
|
||||
},
|
||||
{
|
||||
|
|
|
@ -36,7 +36,7 @@ import { humanizeTimeStamp } from '../humanize_time_stamp';
|
|||
import { DataStreamsBadges } from '../data_stream_badges';
|
||||
import { ConditionalWrap } from '../data_stream_detail_panel';
|
||||
import { isDataStreamFullyManagedByILM } from '../../../../lib/data_streams';
|
||||
import { getIndexModeLabel } from '../../../../lib/index_mode_labels';
|
||||
import { indexModeLabels } from '../../../../lib/index_mode_labels';
|
||||
import { FilterListButton, Filters } from '../../components';
|
||||
import { type DataStreamFilterName } from '../data_stream_list';
|
||||
|
||||
|
@ -192,7 +192,7 @@ export const DataStreamTable: React.FunctionComponent<Props> = ({
|
|||
}),
|
||||
truncateText: true,
|
||||
sortable: true,
|
||||
render: (indexMode: DataStream['indexMode']) => getIndexModeLabel(indexMode),
|
||||
render: (indexMode: DataStream['indexMode']) => indexModeLabels[indexMode],
|
||||
});
|
||||
|
||||
columns.push({
|
||||
|
|
|
@ -28,7 +28,7 @@ import { TemplateDeserialized } from '../../../../../../../common';
|
|||
import { ILM_PAGES_POLICY_EDIT } from '../../../../../constants';
|
||||
import { useIlmLocator } from '../../../../../services/use_ilm_locator';
|
||||
import { allowAutoCreateRadioIds } from '../../../../../../../common/constants';
|
||||
import { getIndexModeLabel } from '../../../../../lib/index_mode_labels';
|
||||
import { indexModeLabels } from '../../../../../lib/index_mode_labels';
|
||||
|
||||
interface Props {
|
||||
templateDetails: TemplateDeserialized;
|
||||
|
@ -54,11 +54,11 @@ export const TabSummary: React.FunctionComponent<Props> = ({ templateDetails })
|
|||
composedOf,
|
||||
order,
|
||||
indexPatterns = [],
|
||||
indexMode,
|
||||
ilmPolicy,
|
||||
_meta,
|
||||
_kbnMeta: { isLegacy, hasDatastream },
|
||||
allowAutoCreate,
|
||||
template,
|
||||
} = templateDetails;
|
||||
|
||||
const numIndexPatterns = indexPatterns.length;
|
||||
|
@ -231,7 +231,7 @@ export const TabSummary: React.FunctionComponent<Props> = ({ templateDetails })
|
|||
/>
|
||||
</EuiDescriptionListTitle>
|
||||
<EuiDescriptionListDescription>
|
||||
{getIndexModeLabel(template?.settings?.index?.mode)}
|
||||
{indexModeLabels[indexMode]}
|
||||
</EuiDescriptionListDescription>
|
||||
|
||||
{/* Allow auto create */}
|
||||
|
|
|
@ -13,6 +13,7 @@ export const templateSchema = schema.object({
|
|||
version: schema.maybe(schema.number()),
|
||||
order: schema.maybe(schema.number()),
|
||||
priority: schema.maybe(schema.number()),
|
||||
indexMode: schema.string(),
|
||||
// Not present for legacy templates
|
||||
allowAutoCreate: schema.maybe(schema.string()),
|
||||
template: schema.maybe(
|
||||
|
|
|
@ -23,6 +23,7 @@ export const getComposableTemplate = ({
|
|||
isLegacy = false,
|
||||
type = 'default',
|
||||
allowAutoCreate = 'NO_OVERWRITE',
|
||||
indexMode = 'standard',
|
||||
composedOf = [],
|
||||
}: Partial<
|
||||
TemplateDeserialized & {
|
||||
|
@ -42,6 +43,7 @@ export const getComposableTemplate = ({
|
|||
version,
|
||||
priority,
|
||||
indexPatterns,
|
||||
indexMode,
|
||||
allowAutoCreate,
|
||||
template: {
|
||||
aliases,
|
||||
|
@ -66,6 +68,7 @@ export const getTemplate = ({
|
|||
order = getRandomNumber(),
|
||||
indexPatterns = [],
|
||||
template: { settings, aliases, mappings } = {},
|
||||
indexMode = 'standard',
|
||||
dataStream,
|
||||
composedOf,
|
||||
ignoreMissingComponentTemplates,
|
||||
|
@ -85,6 +88,7 @@ export const getTemplate = ({
|
|||
version,
|
||||
order,
|
||||
indexPatterns,
|
||||
indexMode,
|
||||
allowAutoCreate,
|
||||
template: {
|
||||
aliases,
|
||||
|
|
|
@ -59,6 +59,7 @@ export function templatesHelpers(getService: FtrProviderContext['getService']) {
|
|||
name,
|
||||
indexPatterns,
|
||||
version: 1,
|
||||
indexMode: 'standard',
|
||||
template: { ...getTemplateMock(isMappingsSourceFieldEnabled) },
|
||||
_kbnMeta: {
|
||||
isLegacy,
|
||||
|
|
|
@ -92,6 +92,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const expectedKeys = [
|
||||
'name',
|
||||
'indexPatterns',
|
||||
'indexMode',
|
||||
'hasSettings',
|
||||
'hasAliases',
|
||||
'hasMappings',
|
||||
|
@ -115,6 +116,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const expectedLegacyKeys = [
|
||||
'name',
|
||||
'indexPatterns',
|
||||
'indexMode',
|
||||
'hasSettings',
|
||||
'hasAliases',
|
||||
'hasMappings',
|
||||
|
@ -138,6 +140,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const expectedWithDSLKeys = [
|
||||
'name',
|
||||
'indexPatterns',
|
||||
'indexMode',
|
||||
'lifecycle',
|
||||
'hasSettings',
|
||||
'hasAliases',
|
||||
|
@ -163,6 +166,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const expectedWithILMKeys = [
|
||||
'name',
|
||||
'indexPatterns',
|
||||
'indexMode',
|
||||
'ilmPolicy',
|
||||
'hasSettings',
|
||||
'hasAliases',
|
||||
|
@ -190,6 +194,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const expectedKeys = [
|
||||
'name',
|
||||
'indexPatterns',
|
||||
'indexMode',
|
||||
'template',
|
||||
'composedOf',
|
||||
'ignoreMissingComponentTemplates',
|
||||
|
@ -213,6 +218,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const expectedKeys = [
|
||||
'name',
|
||||
'indexPatterns',
|
||||
'indexMode',
|
||||
'template',
|
||||
'order',
|
||||
'version',
|
||||
|
@ -375,6 +381,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
_kbnMeta: { hasDatastream: false, type: 'default' },
|
||||
name: templateName,
|
||||
indexPatterns: [getRandomString()],
|
||||
indexMode: 'standard',
|
||||
template: {},
|
||||
deprecated: true,
|
||||
allowAutoCreate: 'TRUE',
|
||||
|
|
|
@ -67,6 +67,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
const stepTitle = await testSubjects.getVisibleText('stepTitle');
|
||||
expect(stepTitle).to.be('Index settings (optional)');
|
||||
|
||||
// Verify that index mode callout is displayed
|
||||
const indexModeCalloutText = await testSubjects.getVisibleText('indexModeCallout');
|
||||
expect(indexModeCalloutText).to.be(
|
||||
'The index.mode setting has been set to Standard within template Logistics. Any changes to index.mode set on this page will be overwritten by the Logistics selection.'
|
||||
);
|
||||
|
||||
// Click Next button
|
||||
await pageObjects.indexManagement.clickNextButton();
|
||||
});
|
||||
|
|
|
@ -56,6 +56,7 @@ export function SvlTemplatesHelpers({ getService }: FtrProviderContext) {
|
|||
const baseTemplate: TemplateDeserialized = {
|
||||
name,
|
||||
indexPatterns,
|
||||
indexMode: 'standard',
|
||||
version: 1,
|
||||
template: { ...getTemplateMock(isMappingsSourceFieldEnabled) },
|
||||
_kbnMeta: {
|
||||
|
|
|
@ -90,6 +90,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const expectedKeys = [
|
||||
'name',
|
||||
'indexPatterns',
|
||||
'indexMode',
|
||||
'hasSettings',
|
||||
'hasAliases',
|
||||
'hasMappings',
|
||||
|
@ -114,6 +115,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const expectedKeys = [
|
||||
'name',
|
||||
'indexPatterns',
|
||||
'indexMode',
|
||||
'template',
|
||||
'_kbnMeta',
|
||||
'allowAutoCreate',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue