diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx index 6ba2454025beb..2897551a209b2 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx @@ -71,6 +71,10 @@ describe('', () => { const templateToEdit = fixtures.getTemplate({ name: 'index_template_without_mappings', indexPatterns: ['indexPattern1'], + dataStream: { + hidden: true, + anyUnknownKey: 'should_be_kept', + }, }); beforeAll(() => { @@ -85,7 +89,7 @@ describe('', () => { testBed.component.update(); }); - it('allows you to add mappings', async () => { + test('allows you to add mappings', async () => { const { actions, find } = testBed; // Logistics await actions.completeStepOne(); @@ -98,6 +102,47 @@ describe('', () => { expect(find('fieldsListItem').length).toBe(1); }); + + test('should keep data stream configuration', async () => { + const { actions } = testBed; + // Logistics + await actions.completeStepOne({ + name: 'test', + indexPatterns: ['myPattern*'], + version: 1, + }); + // Component templates + await actions.completeStepTwo(); + // Index settings + await actions.completeStepThree(); + // Mappings + await actions.completeStepFour(); + // Aliases + await actions.completeStepFive(); + + await act(async () => { + actions.clickNextButton(); + }); + + const latestRequest = server.requests[server.requests.length - 1]; + + const expected = { + name: 'test', + indexPatterns: ['myPattern*'], + dataStream: { + hidden: true, + anyUnknownKey: 'should_be_kept', + }, + version: 1, + _kbnMeta: { + type: 'default', + isLegacy: false, + hasDatastream: true, + }, + }; + + expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual(expected); + }); }); describe('with mappings', () => { diff --git a/x-pack/plugins/index_management/common/types/templates.ts b/x-pack/plugins/index_management/common/types/templates.ts index d1b51fe5b89bf..7b442b9dd2935 100644 --- a/x-pack/plugins/index_management/common/types/templates.ts +++ b/x-pack/plugins/index_management/common/types/templates.ts @@ -46,7 +46,11 @@ export interface TemplateDeserialized { name: string; }; _meta?: { [key: string]: any }; // Composable template only - dataStream?: {}; // Composable template only + // Composable template only + dataStream?: { + hidden?: boolean; + [key: string]: any; + }; _kbnMeta: { type: TemplateType; hasDatastream: boolean; diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx index 89e857eec0bb3..bbc3656195470 100644 --- a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx @@ -55,7 +55,7 @@ function getFieldsMeta(esDocsBase: string) { ), testSubject: 'indexPatternsField', }, - dataStream: { + createDataStream: { title: i18n.translate('xpack.idxMgmt.templateForm.stepLogistics.dataStreamTitle', { defaultMessage: 'Data stream', }), @@ -119,6 +119,7 @@ interface LogisticsForm { interface LogisticsFormInternal extends LogisticsForm { addMeta: boolean; + doCreateDataStream: boolean; } interface Props { @@ -132,12 +133,16 @@ function formDeserializer(formData: LogisticsForm): LogisticsFormInternal { return { ...formData, addMeta: Boolean(formData._meta && Object.keys(formData._meta).length), + doCreateDataStream: Boolean(formData.dataStream), }; } -function formSerializer(formData: LogisticsFormInternal): LogisticsForm { - const { addMeta, ...rest } = formData; - return rest; +function getformSerializer(initialTemplateData: LogisticsForm = {}) { + return (formData: LogisticsFormInternal): LogisticsForm => { + const { addMeta, doCreateDataStream, ...rest } = formData; + const dataStream = doCreateDataStream ? initialTemplateData.dataStream ?? {} : undefined; + return { ...rest, dataStream }; + }; } export const StepLogistics: React.FunctionComponent = React.memo( @@ -146,7 +151,7 @@ export const StepLogistics: React.FunctionComponent = React.memo( schema: schemas.logistics, defaultValue, options: { stripEmptyFields: false }, - serializer: formSerializer, + serializer: getformSerializer(defaultValue), deserializer: formDeserializer, }); const { @@ -178,7 +183,7 @@ export const StepLogistics: React.FunctionComponent = React.memo( }); }, [onChange, isFormValid, validate, getFormData]); - const { name, indexPatterns, dataStream, order, priority, version } = getFieldsMeta( + const { name, indexPatterns, createDataStream, order, priority, version } = getFieldsMeta( documentationService.getEsDocsBase() ); @@ -245,10 +250,10 @@ export const StepLogistics: React.FunctionComponent = React.memo( {/* Create data stream */} {isLegacy !== true && ( - + )} diff --git a/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx index c85126f08685e..2bc146c118ba2 100644 --- a/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx @@ -129,31 +129,12 @@ export const schemas: Record = { }, ], }, - dataStream: { + doCreateDataStream: { type: FIELD_TYPES.TOGGLE, label: i18n.translate('xpack.idxMgmt.templateForm.stepLogistics.datastreamLabel', { defaultMessage: 'Create data stream', }), defaultValue: false, - serializer: (value) => { - if (value === true) { - // For now, ES expects an empty object when defining a data stream - // https://github.com/elastic/elasticsearch/pull/59317 - return {}; - } - }, - deserializer: (value) => { - if (typeof value === 'boolean') { - return value; - } - - /** - * For now, it is enough to have a "data_stream" declared on the index template - * to assume that the template creates a data stream. In the future, this condition - * might change - */ - return value !== undefined; - }, }, order: { type: FIELD_TYPES.NUMBER, diff --git a/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts b/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts index 18c74716a35b6..3dab4113e6965 100644 --- a/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts +++ b/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts @@ -20,7 +20,14 @@ export const templateSchema = schema.object({ }) ), composedOf: schema.maybe(schema.arrayOf(schema.string())), - dataStream: schema.maybe(schema.object({}, { unknowns: 'allow' })), + dataStream: schema.maybe( + schema.object( + { + hidden: schema.maybe(schema.boolean()), + }, + { unknowns: 'allow' } + ) + ), _meta: schema.maybe(schema.object({}, { unknowns: 'allow' })), ilmPolicy: schema.maybe( schema.object({ diff --git a/x-pack/plugins/index_management/test/fixtures/template.ts b/x-pack/plugins/index_management/test/fixtures/template.ts index 016100faea601..90f556794a5d9 100644 --- a/x-pack/plugins/index_management/test/fixtures/template.ts +++ b/x-pack/plugins/index_management/test/fixtures/template.ts @@ -53,6 +53,7 @@ export const getTemplate = ({ order = getRandomNumber(), indexPatterns = [], template: { settings, aliases, mappings } = {}, + dataStream, hasDatastream = false, isLegacy = false, type = 'default', @@ -73,12 +74,13 @@ export const getTemplate = ({ mappings, settings, }, + dataStream, hasSettings: objHasProperties(settings), hasMappings: objHasProperties(mappings), hasAliases: objHasProperties(aliases), _kbnMeta: { type, - hasDatastream, + hasDatastream: dataStream !== undefined ? true : hasDatastream, isLegacy, }, };