From ec1bd97422fbde94b07cc8d9dee4102a2ef76f0e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Wed, 25 Mar 2020 15:12:35 +0100
Subject: [PATCH 01/31] Add helper method to get template format version (1 or
2)
---
.../plugins/index_management/common/index.ts | 2 ++
.../common/types/templates.ts | 21 +++++++++---
.../index_management/server/lib/utils.test.ts | 33 +++++++++++++++++++
.../index_management/server/lib/utils.ts | 16 +++++++++
4 files changed, 68 insertions(+), 4 deletions(-)
create mode 100644 x-pack/plugins/index_management/server/lib/utils.test.ts
create mode 100644 x-pack/plugins/index_management/server/lib/utils.ts
diff --git a/x-pack/plugins/index_management/common/index.ts b/x-pack/plugins/index_management/common/index.ts
index 0cc4ba79711ce..ac9db87c6a72b 100644
--- a/x-pack/plugins/index_management/common/index.ts
+++ b/x-pack/plugins/index_management/common/index.ts
@@ -5,3 +5,5 @@
*/
export { PLUGIN, API_BASE_PATH } from './constants';
+
+export * from './types';
diff --git a/x-pack/plugins/index_management/common/types/templates.ts b/x-pack/plugins/index_management/common/types/templates.ts
index e31c10a775156..cdeaa2429296c 100644
--- a/x-pack/plugins/index_management/common/types/templates.ts
+++ b/x-pack/plugins/index_management/common/types/templates.ts
@@ -17,20 +17,33 @@ export interface TemplateListItem {
};
isManaged: boolean;
}
-export interface Template {
+interface TemplateBase {
name: string;
indexPatterns: string[];
version?: number;
order?: number;
- settings?: object;
- aliases?: object;
- mappings?: object;
ilmPolicy?: {
name: string;
};
isManaged: boolean;
}
+export interface TemplateV1 extends TemplateBase {
+ settings?: object;
+ aliases?: object;
+ mappings?: object;
+}
+
+export interface TemplateV2 extends TemplateBase {
+ template: {
+ settings?: object;
+ aliases?: object;
+ mappings?: object;
+ };
+}
+
+export type Template = TemplateV1 | TemplateV2;
+
export interface TemplateEs {
name: string;
index_patterns: string[];
diff --git a/x-pack/plugins/index_management/server/lib/utils.test.ts b/x-pack/plugins/index_management/server/lib/utils.test.ts
new file mode 100644
index 0000000000000..7ec3313a68a51
--- /dev/null
+++ b/x-pack/plugins/index_management/server/lib/utils.test.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { TemplateV1, TemplateV2 } from '../../common';
+import { getTemplateVersion } from './utils';
+
+describe('utils', () => {
+ describe('getTemplateVersion', () => {
+ test('should detect v1 template', () => {
+ const template = {
+ indexPatterns: ['logs*'],
+ mappings: {
+ properties: {},
+ },
+ };
+ expect(getTemplateVersion(template as TemplateV1)).toBe(1);
+ });
+
+ test('should detect v2 template', () => {
+ const template = {
+ indexPatterns: ['logs*'],
+ template: {
+ mappings: {
+ properties: {},
+ },
+ },
+ };
+ expect(getTemplateVersion(template as TemplateV2)).toBe(2);
+ });
+ });
+});
diff --git a/x-pack/plugins/index_management/server/lib/utils.ts b/x-pack/plugins/index_management/server/lib/utils.ts
new file mode 100644
index 0000000000000..3fff95b4ce34f
--- /dev/null
+++ b/x-pack/plugins/index_management/server/lib/utils.ts
@@ -0,0 +1,16 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { Template } from '../../common';
+
+/**
+ * Helper to get the format version of an index template.
+ * v1 will be supported up until 9.x but marked as deprecated from 7.8
+ * v2 will be supported from 7.8
+ */
+export const getTemplateVersion = (template: Template): 1 | 2 => {
+ return {}.hasOwnProperty.call(template, 'template') ? 2 : 1;
+};
From 1a91c0071aebea32ed5698160b5ff73d6bdb2a9e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Wed, 25 Mar 2020 15:56:29 +0100
Subject: [PATCH 02/31] Rename interface to make explicit deserialized template
vs serialized one
---
.../client_integration/helpers/home.helpers.ts | 13 ++++++++-----
.../helpers/template_form.helpers.ts | 4 ++--
.../common/lib/template_serialization.ts | 12 ++++++------
.../index_management/common/types/templates.ts | 8 ++++++--
.../components/mappings_editor/reducer.ts | 4 ++--
.../components/template_delete_modal.tsx | 4 ++--
.../template_form/steps/step_review.tsx | 4 ++--
.../components/template_form/template_form.tsx | 13 ++++++++-----
.../components/template_form/types.ts | 4 ++--
.../template_details/tabs/tab_aliases.tsx | 4 ++--
.../template_details/tabs/tab_mappings.tsx | 4 ++--
.../template_details/tabs/tab_settings.tsx | 4 ++--
.../template_details/tabs/tab_summary.tsx | 4 ++--
.../template_details/template_details.tsx | 12 ++++++------
.../home/template_list/template_list.tsx | 12 +++++++-----
.../template_table/template_table.tsx | 18 +++++++++---------
.../sections/template_clone/template_clone.tsx | 6 +++---
.../template_create/template_create.tsx | 4 ++--
.../sections/template_edit/template_edit.tsx | 4 ++--
.../public/application/services/api.ts | 10 +++++-----
.../index_management/server/lib/utils.ts | 4 ++--
.../api/templates/register_create_route.ts | 6 +++---
.../api/templates/register_delete_route.ts | 4 ++--
.../api/templates/register_update_route.ts | 6 +++---
.../index_management/test/fixtures/template.ts | 4 ++--
25 files changed, 92 insertions(+), 80 deletions(-)
diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts
index 7e3e1fba9c44a..397a78354f470 100644
--- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts
+++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts
@@ -16,7 +16,7 @@ import {
import { IndexManagementHome } from '../../../public/application/sections/home'; // eslint-disable-line @kbn/eslint/no-restricted-paths
import { BASE_PATH } from '../../../common/constants';
import { indexManagementStore } from '../../../public/application/store'; // eslint-disable-line @kbn/eslint/no-restricted-paths
-import { Template } from '../../../common/types';
+import { TemplateDeserialized } from '../../../common';
import { WithAppDependencies, services } from './setup_environment';
const testBedConfig: TestBedConfig = {
@@ -36,10 +36,13 @@ export interface IdxMgmtHomeTestBed extends TestBed {
selectHomeTab: (tab: 'indicesTab' | 'templatesTab') => void;
selectDetailsTab: (tab: 'summary' | 'settings' | 'mappings' | 'aliases') => void;
clickReloadButton: () => void;
- clickTemplateAction: (name: Template['name'], action: 'edit' | 'clone' | 'delete') => void;
+ clickTemplateAction: (
+ name: TemplateDeserialized['name'],
+ action: 'edit' | 'clone' | 'delete'
+ ) => void;
clickTemplateAt: (index: number) => void;
clickCloseDetailsButton: () => void;
- clickActionMenu: (name: Template['name']) => void;
+ clickActionMenu: (name: TemplateDeserialized['name']) => void;
};
}
@@ -78,7 +81,7 @@ export const setup = async (): Promise => {
find('reloadButton').simulate('click');
};
- const clickActionMenu = async (templateName: Template['name']) => {
+ const clickActionMenu = async (templateName: TemplateDeserialized['name']) => {
const { component } = testBed;
// When a table has > 2 actions, EUI displays an overflow menu with an id "-actions"
@@ -87,7 +90,7 @@ export const setup = async (): Promise => {
};
const clickTemplateAction = (
- templateName: Template['name'],
+ templateName: TemplateDeserialized['name'],
action: 'edit' | 'clone' | 'delete'
) => {
const actions = ['edit', 'clone', 'delete'];
diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts
index 9d4eb631a1c40..9d62ed6bdbe5f 100644
--- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts
+++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { TestBed, SetupFunc, UnwrapPromise } from '../../../../../test_utils';
-import { Template } from '../../../common/types';
+import { TemplateDeserialized } from '../../../common';
import { nextTick } from './index';
interface MappingField {
@@ -62,7 +62,7 @@ export const formSetup = async (initTestBed: SetupFunc) => {
indexPatterns,
order,
version,
- }: Partial = {}) => {
+ }: Partial = {}) => {
const { form, find, component } = testBed;
if (name) {
diff --git a/x-pack/plugins/index_management/common/lib/template_serialization.ts b/x-pack/plugins/index_management/common/lib/template_serialization.ts
index b7d3410c775d4..fdfc9720ae1f8 100644
--- a/x-pack/plugins/index_management/common/lib/template_serialization.ts
+++ b/x-pack/plugins/index_management/common/lib/template_serialization.ts
@@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import { Template, TemplateEs, TemplateListItem } from '../types';
+import { TemplateDeserialized, TemplateSerialized, TemplateListItem } from '../types';
const hasEntries = (data: object = {}) => Object.entries(data).length > 0;
@@ -39,10 +39,10 @@ export function deserializeTemplateList(
return deserializedTemplates;
}
-export function serializeTemplate(template: Template): TemplateEs {
+export function serializeTemplate(template: TemplateDeserialized): TemplateSerialized {
const { name, version, order, indexPatterns, settings, aliases, mappings } = template;
- const serializedTemplate: TemplateEs = {
+ const serializedTemplate: TemplateSerialized = {
name,
version,
order,
@@ -56,9 +56,9 @@ export function serializeTemplate(template: Template): TemplateEs {
}
export function deserializeTemplate(
- templateEs: TemplateEs,
+ templateEs: TemplateSerialized,
managedTemplatePrefix?: string
-): Template {
+): TemplateDeserialized {
const {
name,
version,
@@ -69,7 +69,7 @@ export function deserializeTemplate(
mappings,
} = templateEs;
- const deserializedTemplate: Template = {
+ const deserializedTemplate: TemplateDeserialized = {
name,
version,
order,
diff --git a/x-pack/plugins/index_management/common/types/templates.ts b/x-pack/plugins/index_management/common/types/templates.ts
index cdeaa2429296c..14ec8ddaff56b 100644
--- a/x-pack/plugins/index_management/common/types/templates.ts
+++ b/x-pack/plugins/index_management/common/types/templates.ts
@@ -42,9 +42,13 @@ export interface TemplateV2 extends TemplateBase {
};
}
-export type Template = TemplateV1 | TemplateV2;
+export interface TemplateDeserialized extends TemplateV1 {
+ _kbnMeta?: {
+ version: 1 | 2;
+ };
+}
-export interface TemplateEs {
+export interface TemplateSerialized {
name: string;
index_patterns: string[];
version?: number;
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts
index 1e6733b1632d7..a799f9ddcbc2b 100644
--- a/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts
@@ -33,10 +33,10 @@ export interface MappingsConfiguration {
}
export interface MappingsTemplates {
- dynamic_templates: Template[];
+ dynamic_templates: TemplateDeserialized[];
}
-interface Template {
+interface TemplateDeserialized {
[key: string]: any;
}
diff --git a/x-pack/plugins/index_management/public/application/components/template_delete_modal.tsx b/x-pack/plugins/index_management/public/application/components/template_delete_modal.tsx
index 4e36643dbe117..d82024c9aabba 100644
--- a/x-pack/plugins/index_management/public/application/components/template_delete_modal.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_delete_modal.tsx
@@ -10,13 +10,13 @@ import { FormattedMessage } from '@kbn/i18n/react';
import React, { Fragment, useState } from 'react';
import { deleteTemplates } from '../services/api';
import { notificationService } from '../services/notification';
-import { Template } from '../../../common/types';
+import { TemplateDeserialized } from '../../../common';
export const TemplateDeleteModal = ({
templatesToDelete,
callback,
}: {
- templatesToDelete: Array;
+ templatesToDelete: Array;
callback: (data?: { hasDeletedTemplates: boolean }) => void;
}) => {
const [isDeleteConfirmed, setIsDeleteConfirmed] = useState(false);
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
index 09da43b83c3c5..9cc2bb436f5a9 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
@@ -23,7 +23,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { serializers } from '../../../../shared_imports';
import { serializeTemplate } from '../../../../../common/lib/template_serialization';
-import { Template } from '../../../../../common/types';
+import { TemplateDeserialized } from '../../../../../common';
import { StepProps } from '../types';
const { stripEmptyFields } = serializers;
@@ -54,7 +54,7 @@ const getDescriptionText = (data: any) => {
export const StepReview: React.FunctionComponent = ({ template, updateCurrentStep }) => {
const { name, indexPatterns, version, order } = template;
- const serializedTemplate = serializeTemplate(stripEmptyFields(template) as Template);
+ const serializedTemplate = serializeTemplate(stripEmptyFields(template) as TemplateDeserialized);
// Name not included in ES request body
delete serializedTemplate.name;
const {
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
index 58be9b2c63365..f6ef5961091c3 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
@@ -15,7 +15,7 @@ import {
} from '@elastic/eui';
import { serializers } from '../../../shared_imports';
-import { Template } from '../../../../common/types';
+import { TemplateDeserialized } from '../../../../common';
import { TemplateSteps } from './template_steps';
import { StepAliases, StepLogistics, StepMappings, StepSettings, StepReview } from './steps';
import { StepProps, DataGetterFunc } from './types';
@@ -24,11 +24,11 @@ import { SectionError } from '../section_error';
const { stripEmptyFields } = serializers;
interface Props {
- onSave: (template: Template) => void;
+ onSave: (template: TemplateDeserialized) => void;
clearSaveError: () => void;
isSaving: boolean;
saveError: any;
- defaultValue?: Template;
+ defaultValue?: TemplateDeserialized;
isEditing?: boolean;
}
@@ -63,7 +63,7 @@ export const TemplateForm: React.FunctionComponent = ({
5: defaultValidation,
});
- const template = useRef>(defaultValue);
+ const template = useRef>(defaultValue);
const stepsDataGetters = useRef>({});
const lastStep = Object.keys(stepComponentMap).length;
@@ -222,7 +222,10 @@ export const TemplateForm: React.FunctionComponent = ({
fill
color="secondary"
iconType="check"
- onClick={onSave.bind(null, stripEmptyFields(template.current) as Template)}
+ onClick={onSave.bind(
+ null,
+ stripEmptyFields(template.current) as TemplateDeserialized
+ )}
data-test-subj="submitButton"
isLoading={isSaving}
>
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/types.ts b/x-pack/plugins/index_management/public/application/components/template_form/types.ts
index 9385f0c9f738b..edeb67b2ada18 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/types.ts
+++ b/x-pack/plugins/index_management/public/application/components/template_form/types.ts
@@ -4,10 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { Template } from '../../../../common/types';
+import { TemplateDeserialized } from '../../../../common';
export interface StepProps {
- template: Partial;
+ template: Partial;
setDataGetter: (dataGetter: DataGetterFunc) => void;
updateCurrentStep: (step: number) => void;
onStepValidityChange: (isValid: boolean | undefined) => void;
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx
index 421119bd8df96..0dd1b78529724 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx
@@ -7,10 +7,10 @@
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiCodeBlock, EuiCallOut } from '@elastic/eui';
-import { Template } from '../../../../../../../common/types';
+import { TemplateDeserialized } from '../../../../../../../common';
interface Props {
- templateDetails: Template;
+ templateDetails: TemplateDeserialized;
}
export const TabAliases: React.FunctionComponent = ({ templateDetails }) => {
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx
index 83f2e67fb12c5..3e07ced74a251 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx
@@ -7,10 +7,10 @@
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiCodeBlock, EuiCallOut } from '@elastic/eui';
-import { Template } from '../../../../../../../common/types';
+import { TemplateDeserialized } from '../../../../../../../common';
interface Props {
- templateDetails: Template;
+ templateDetails: TemplateDeserialized;
}
export const TabMappings: React.FunctionComponent = ({ templateDetails }) => {
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx
index 8b2a431bee65a..f029a7db3c39a 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx
@@ -7,10 +7,10 @@
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiCodeBlock, EuiCallOut } from '@elastic/eui';
-import { Template } from '../../../../../../../common/types';
+import { TemplateDeserialized } from '../../../../../../../common';
interface Props {
- templateDetails: Template;
+ templateDetails: TemplateDeserialized;
}
export const TabSettings: React.FunctionComponent = ({ templateDetails }) => {
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx
index 99f5db54b4ba2..0ee2dad6aef72 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx
@@ -14,11 +14,11 @@ import {
EuiText,
EuiTitle,
} from '@elastic/eui';
-import { Template } from '../../../../../../../common/types';
+import { TemplateDeserialized } from '../../../../../../../common';
import { getILMPolicyPath } from '../../../../../services/navigation';
interface Props {
- templateDetails: Template;
+ templateDetails: TemplateDeserialized;
}
const NoneDescriptionText = () => (
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx
index 9c31b0d650449..3fe058756d0ac 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx
@@ -30,7 +30,7 @@ import {
UIM_TEMPLATE_DETAIL_PANEL_SETTINGS_TAB,
UIM_TEMPLATE_DETAIL_PANEL_ALIASES_TAB,
} from '../../../../../../common/constants';
-import { Template } from '../../../../../../common/types';
+import { TemplateDeserialized } from '../../../../../../common';
import { TemplateDeleteModal, SectionLoading, SectionError, Error } from '../../../../components';
import { useLoadIndexTemplate } from '../../../../services/api';
import { decodePath } from '../../../../services/routing';
@@ -39,10 +39,10 @@ import { useServices } from '../../../../app_context';
import { TabSummary, TabMappings, TabSettings, TabAliases } from './tabs';
interface Props {
- templateName: Template['name'];
+ templateName: TemplateDeserialized['name'];
onClose: () => void;
- editTemplate: (templateName: Template['name']) => void;
- cloneTemplate: (templateName: Template['name']) => void;
+ editTemplate: (templateName: TemplateDeserialized['name']) => void;
+ cloneTemplate: (templateName: TemplateDeserialized['name']) => void;
reload: () => Promise;
}
@@ -79,7 +79,7 @@ const TABS = [
];
const tabToComponentMap: {
- [key: string]: React.FunctionComponent<{ templateDetails: Template }>;
+ [key: string]: React.FunctionComponent<{ templateDetails: TemplateDeserialized }>;
} = {
[SUMMARY_TAB_ID]: TabSummary,
[SETTINGS_TAB_ID]: TabSettings,
@@ -106,7 +106,7 @@ export const TemplateDetails: React.FunctionComponent = ({
const { error, data: templateDetails, isLoading } = useLoadIndexTemplate(decodedTemplateName);
// TS complains if we use destructuring here. Fixed in 3.6.0 (https://github.com/microsoft/TypeScript/pull/31711).
const isManaged = templateDetails ? templateDetails.isManaged : undefined;
- const [templateToDelete, setTemplateToDelete] = useState>([]);
+ const [templateToDelete, setTemplateToDelete] = useState>([]);
const [activeTab, setActiveTab] = useState(SUMMARY_TAB_ID);
const [isPopoverOpen, setIsPopOverOpen] = useState(false);
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
index ffdb224f16271..ca44fd5e9d868 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
@@ -19,7 +19,7 @@ import {
import { SectionError, SectionLoading, Error } from '../../../components';
import { TemplateTable } from './template_table';
import { useLoadIndexTemplates } from '../../../services/api';
-import { Template } from '../../../../../common/types';
+import { TemplateDeserialized } from '../../../../../common';
import { useServices } from '../../../app_context';
import {
getTemplateEditLink,
@@ -30,7 +30,7 @@ import { UIM_TEMPLATE_LIST_LOAD } from '../../../../../common/constants';
import { TemplateDetails } from './template_details';
interface MatchParams {
- templateName?: Template['name'];
+ templateName?: TemplateDeserialized['name'];
}
export const TemplateList: React.FunctionComponent> = ({
@@ -49,7 +49,9 @@ export const TemplateList: React.FunctionComponent
- templates ? templates.filter((template: Template) => !template.name.startsWith('.')) : [],
+ templates
+ ? templates.filter((template: TemplateDeserialized) => !template.name.startsWith('.'))
+ : [],
[templates]
);
@@ -57,11 +59,11 @@ export const TemplateList: React.FunctionComponent {
+ const editTemplate = (name: TemplateDeserialized['name']) => {
history.push(getTemplateEditLink(name));
};
- const cloneTemplate = (name: Template['name']) => {
+ const cloneTemplate = (name: TemplateDeserialized['name']) => {
history.push(getTemplateCloneLink(name));
};
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx
index 0f93e1c9571fd..d895d8a9e86a5 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx
@@ -8,7 +8,7 @@ import React, { useState, Fragment } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiInMemoryTable, EuiIcon, EuiButton, EuiLink, EuiBasicTableColumn } from '@elastic/eui';
-import { TemplateListItem, Template } from '../../../../../../common/types';
+import { TemplateListItem, TemplateDeserialized } from '../../../../../../common';
import { BASE_PATH, UIM_TEMPLATE_SHOW_DETAILS_CLICK } from '../../../../../../common/constants';
import { TemplateDeleteModal } from '../../../../components';
import { useServices } from '../../../../app_context';
@@ -18,8 +18,8 @@ import { SendRequestResponse } from '../../../../../shared_imports';
interface Props {
templates: TemplateListItem[];
reload: () => Promise;
- editTemplate: (name: Template['name']) => void;
- cloneTemplate: (name: Template['name']) => void;
+ editTemplate: (name: TemplateDeserialized['name']) => void;
+ cloneTemplate: (name: TemplateDeserialized['name']) => void;
}
export const TemplateTable: React.FunctionComponent = ({
@@ -133,10 +133,10 @@ export const TemplateTable: React.FunctionComponent = ({
}),
icon: 'pencil',
type: 'icon',
- onClick: ({ name }: Template) => {
+ onClick: ({ name }: TemplateDeserialized) => {
editTemplate(name);
},
- enabled: ({ isManaged }: Template) => !isManaged,
+ enabled: ({ isManaged }: TemplateDeserialized) => !isManaged,
},
{
type: 'icon',
@@ -147,7 +147,7 @@ export const TemplateTable: React.FunctionComponent = ({
defaultMessage: 'Clone this template',
}),
icon: 'copy',
- onClick: ({ name }: Template) => {
+ onClick: ({ name }: TemplateDeserialized) => {
cloneTemplate(name);
},
},
@@ -161,11 +161,11 @@ export const TemplateTable: React.FunctionComponent = ({
icon: 'trash',
color: 'danger',
type: 'icon',
- onClick: ({ name }: Template) => {
+ onClick: ({ name }: TemplateDeserialized) => {
setTemplatesToDelete([name]);
},
isPrimary: true,
- enabled: ({ isManaged }: Template) => !isManaged,
+ enabled: ({ isManaged }: TemplateDeserialized) => !isManaged,
},
],
},
@@ -185,7 +185,7 @@ export const TemplateTable: React.FunctionComponent = ({
const selectionConfig = {
onSelectionChange: setSelection,
- selectable: ({ isManaged }: Template) => !isManaged,
+ selectable: ({ isManaged }: TemplateDeserialized) => !isManaged,
selectableMessage: (selectable: boolean) => {
if (!selectable) {
return i18n.translate('xpack.idxMgmt.templateList.table.deleteManagedTemplateTooltip', {
diff --git a/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx b/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx
index cf6ca3c065777..ff0739177703e 100644
--- a/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx
@@ -10,7 +10,7 @@ import { EuiPageBody, EuiPageContent, EuiSpacer, EuiTitle } from '@elastic/eui';
import { TemplateForm, SectionLoading, SectionError, Error } from '../../components';
import { breadcrumbService } from '../../services/breadcrumbs';
import { decodePath, getTemplateDetailsLink } from '../../services/routing';
-import { Template } from '../../../../common/types';
+import { TemplateDeserialized } from '../../../../common';
import { saveTemplate, useLoadIndexTemplate } from '../../services/api';
interface MatchParams {
@@ -31,7 +31,7 @@ export const TemplateClone: React.FunctionComponent {
+ const onSave = async (template: TemplateDeserialized) => {
setIsSaving(true);
setSaveError(null);
@@ -85,7 +85,7 @@ export const TemplateClone: React.FunctionComponent = ({ h
const [isSaving, setIsSaving] = useState(false);
const [saveError, setSaveError] = useState(null);
- const onSave = async (template: Template) => {
+ const onSave = async (template: TemplateDeserialized) => {
const { name } = template;
setIsSaving(true);
diff --git a/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx b/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx
index 1e9d5f294de34..f4315f3bea8e9 100644
--- a/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx
@@ -11,7 +11,7 @@ import { breadcrumbService } from '../../services/breadcrumbs';
import { useLoadIndexTemplate, updateTemplate } from '../../services/api';
import { decodePath, getTemplateDetailsLink } from '../../services/routing';
import { SectionLoading, SectionError, TemplateForm, Error } from '../../components';
-import { Template } from '../../../../common/types';
+import { TemplateDeserialized } from '../../../../common';
interface MatchParams {
name: string;
@@ -33,7 +33,7 @@ export const TemplateEdit: React.FunctionComponent {
+ const onSave = async (updatedTemplate: TemplateDeserialized) => {
setIsSaving(true);
setSaveError(null);
diff --git a/x-pack/plugins/index_management/public/application/services/api.ts b/x-pack/plugins/index_management/public/application/services/api.ts
index 88887f40972e4..2c2b65a108bab 100644
--- a/x-pack/plugins/index_management/public/application/services/api.ts
+++ b/x-pack/plugins/index_management/public/application/services/api.ts
@@ -37,7 +37,7 @@ import { TAB_SETTINGS, TAB_MAPPING, TAB_STATS } from '../constants';
import { useRequest, sendRequest } from './use_request';
import { httpService } from './http';
import { UiMetricService } from './ui_metric';
-import { Template } from '../../../common/types';
+import { TemplateDeserialized } from '../../../common';
import { IndexMgmtMetricsType } from '../../types';
// Temporary hack to provide the uiMetricService instance to this file.
@@ -207,7 +207,7 @@ export function useLoadIndexTemplates() {
});
}
-export async function deleteTemplates(names: Array) {
+export async function deleteTemplates(names: Array) {
const result = sendRequest({
path: `${API_BASE_PATH}/templates/${names.map(name => encodeURIComponent(name)).join(',')}`,
method: 'delete',
@@ -220,14 +220,14 @@ export async function deleteTemplates(names: Array) {
return result;
}
-export function useLoadIndexTemplate(name: Template['name']) {
+export function useLoadIndexTemplate(name: TemplateDeserialized['name']) {
return useRequest({
path: `${API_BASE_PATH}/templates/${encodeURIComponent(name)}`,
method: 'get',
});
}
-export async function saveTemplate(template: Template, isClone?: boolean) {
+export async function saveTemplate(template: TemplateDeserialized, isClone?: boolean) {
const result = await sendRequest({
path: `${API_BASE_PATH}/templates`,
method: 'put',
@@ -241,7 +241,7 @@ export async function saveTemplate(template: Template, isClone?: boolean) {
return result;
}
-export async function updateTemplate(template: Template) {
+export async function updateTemplate(template: TemplateDeserialized) {
const { name } = template;
const result = await sendRequest({
path: `${API_BASE_PATH}/templates/${encodeURIComponent(name)}`,
diff --git a/x-pack/plugins/index_management/server/lib/utils.ts b/x-pack/plugins/index_management/server/lib/utils.ts
index 3fff95b4ce34f..2fac96d2e05b5 100644
--- a/x-pack/plugins/index_management/server/lib/utils.ts
+++ b/x-pack/plugins/index_management/server/lib/utils.ts
@@ -4,13 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { Template } from '../../common';
+import { TemplateDeserialized } from '../../common';
/**
* Helper to get the format version of an index template.
* v1 will be supported up until 9.x but marked as deprecated from 7.8
* v2 will be supported from 7.8
*/
-export const getTemplateVersion = (template: Template): 1 | 2 => {
+export const getTemplateVersion = (template: TemplateDeserialized): 1 | 2 => {
return {}.hasOwnProperty.call(template, 'template') ? 2 : 1;
};
diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts
index 817893976767f..afb4e910847dd 100644
--- a/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts
+++ b/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts
@@ -6,7 +6,7 @@
import { i18n } from '@kbn/i18n';
-import { Template, TemplateEs } from '../../../../common/types';
+import { TemplateDeserialized, TemplateSerialized } from '../../../../common';
import { serializeTemplate } from '../../../../common/lib';
import { RouteDependencies } from '../../../types';
import { addBasePath } from '../index';
@@ -19,8 +19,8 @@ export function registerCreateRoute({ router, license, lib }: RouteDependencies)
{ path: addBasePath('/templates'), validate: { body: bodySchema } },
license.guardApiRoute(async (ctx, req, res) => {
const { callAsCurrentUser } = ctx.core.elasticsearch.dataClient;
- const template = req.body as Template;
- const serializedTemplate = serializeTemplate(template) as TemplateEs;
+ const template = req.body as TemplateDeserialized;
+ const serializedTemplate = serializeTemplate(template) as TemplateSerialized;
const {
name,
diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_delete_route.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_delete_route.ts
index c9f1995204d8c..39b3b4ea9d8ef 100644
--- a/x-pack/plugins/index_management/server/routes/api/templates/register_delete_route.ts
+++ b/x-pack/plugins/index_management/server/routes/api/templates/register_delete_route.ts
@@ -10,7 +10,7 @@ import { RouteDependencies } from '../../../types';
import { addBasePath } from '../index';
import { wrapEsError } from '../../helpers';
-import { Template } from '../../../../common/types';
+import { TemplateDeserialized } from '../../../../common';
const paramsSchema = schema.object({
names: schema.string(),
@@ -22,7 +22,7 @@ export function registerDeleteRoute({ router, license }: RouteDependencies) {
license.guardApiRoute(async (ctx, req, res) => {
const { names } = req.params as typeof paramsSchema.type;
const templateNames = names.split(',');
- const response: { templatesDeleted: Array; errors: any[] } = {
+ const response: { templatesDeleted: Array; errors: any[] } = {
templatesDeleted: [],
errors: [],
};
diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts
index e7f541fa67f8a..cfcaf19f9c9bf 100644
--- a/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts
+++ b/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts
@@ -5,7 +5,7 @@
*/
import { schema } from '@kbn/config-schema';
-import { Template, TemplateEs } from '../../../../common/types';
+import { TemplateDeserialized, TemplateSerialized } from '../../../../common';
import { serializeTemplate } from '../../../../common/lib';
import { RouteDependencies } from '../../../types';
import { addBasePath } from '../index';
@@ -25,8 +25,8 @@ export function registerUpdateRoute({ router, license, lib }: RouteDependencies)
license.guardApiRoute(async (ctx, req, res) => {
const { callAsCurrentUser } = ctx.core.elasticsearch.dataClient;
const { name } = req.params as typeof paramsSchema.type;
- const template = req.body as Template;
- const serializedTemplate = serializeTemplate(template) as TemplateEs;
+ const template = req.body as TemplateDeserialized;
+ const serializedTemplate = serializeTemplate(template) as TemplateSerialized;
const { order, index_patterns, version, settings, mappings, aliases } = serializedTemplate;
diff --git a/x-pack/plugins/index_management/test/fixtures/template.ts b/x-pack/plugins/index_management/test/fixtures/template.ts
index 19a6e0c257647..b3954776249fc 100644
--- a/x-pack/plugins/index_management/test/fixtures/template.ts
+++ b/x-pack/plugins/index_management/test/fixtures/template.ts
@@ -5,7 +5,7 @@
*/
import { getRandomString, getRandomNumber } from '../../../../test_utils';
-import { Template } from '../../common/types';
+import { TemplateDeserialized } from '../../common';
export const getTemplate = ({
name = getRandomString(),
@@ -16,7 +16,7 @@ export const getTemplate = ({
aliases,
mappings,
isManaged = false,
-}: Partial = {}): Template => ({
+}: Partial = {}): TemplateDeserialized => ({
name,
version,
order,
From c98c1a5d88e64e5a5bc8e1a7e98183ff6c81e1ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Wed, 25 Mar 2020 16:14:54 +0100
Subject: [PATCH 03/31] Refactor template list deserialization
---
.../common/lib/template_serialization.ts | 50 +++++++------------
1 file changed, 18 insertions(+), 32 deletions(-)
diff --git a/x-pack/plugins/index_management/common/lib/template_serialization.ts b/x-pack/plugins/index_management/common/lib/template_serialization.ts
index fdfc9720ae1f8..1e07eb4a1ebaa 100644
--- a/x-pack/plugins/index_management/common/lib/template_serialization.ts
+++ b/x-pack/plugins/index_management/common/lib/template_serialization.ts
@@ -7,38 +7,6 @@ import { TemplateDeserialized, TemplateSerialized, TemplateListItem } from '../t
const hasEntries = (data: object = {}) => Object.entries(data).length > 0;
-export function deserializeTemplateList(
- indexTemplatesByName: any,
- managedTemplatePrefix?: string
-): TemplateListItem[] {
- const indexTemplateNames: string[] = Object.keys(indexTemplatesByName);
-
- const deserializedTemplates: TemplateListItem[] = indexTemplateNames.map((name: string) => {
- const {
- version,
- order,
- index_patterns: indexPatterns = [],
- settings = {},
- aliases = {},
- mappings = {},
- } = indexTemplatesByName[name];
-
- return {
- name,
- version,
- order,
- indexPatterns: indexPatterns.sort(),
- hasSettings: hasEntries(settings),
- hasAliases: hasEntries(aliases),
- hasMappings: hasEntries(mappings),
- ilmPolicy: settings && settings.index && settings.index.lifecycle,
- isManaged: Boolean(managedTemplatePrefix && name.startsWith(managedTemplatePrefix)),
- };
- });
-
- return deserializedTemplates;
-}
-
export function serializeTemplate(template: TemplateDeserialized): TemplateSerialized {
const { name, version, order, indexPatterns, settings, aliases, mappings } = template;
@@ -83,3 +51,21 @@ export function deserializeTemplate(
return deserializedTemplate;
}
+
+export function deserializeTemplateList(
+ indexTemplatesByName: { [key: string]: TemplateSerialized },
+ managedTemplatePrefix?: string
+): TemplateListItem[] {
+ return Object.values(indexTemplatesByName).map(templateSerialized => {
+ const { mappings, settings, aliases, ...deserializedTemplate } = deserializeTemplate(
+ templateSerialized
+ );
+
+ return {
+ ...deserializedTemplate,
+ hasSettings: hasEntries(settings),
+ hasAliases: hasEntries(aliases),
+ hasMappings: hasEntries(mappings),
+ };
+ });
+}
From 57fb49469ad765de0739d705837bc53c1867c598 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Wed, 25 Mar 2020 16:53:23 +0100
Subject: [PATCH 04/31] Move utils to common folder
---
.../{server => common}/lib/utils.test.ts | 12 +++++++-----
.../index_management/{server => common}/lib/utils.ts | 6 ++++--
2 files changed, 11 insertions(+), 7 deletions(-)
rename x-pack/plugins/index_management/{server => common}/lib/utils.test.ts (66%)
rename x-pack/plugins/index_management/{server => common}/lib/utils.ts (68%)
diff --git a/x-pack/plugins/index_management/server/lib/utils.test.ts b/x-pack/plugins/index_management/common/lib/utils.test.ts
similarity index 66%
rename from x-pack/plugins/index_management/server/lib/utils.test.ts
rename to x-pack/plugins/index_management/common/lib/utils.test.ts
index 7ec3313a68a51..221d1b009cede 100644
--- a/x-pack/plugins/index_management/server/lib/utils.test.ts
+++ b/x-pack/plugins/index_management/common/lib/utils.test.ts
@@ -3,31 +3,33 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import { TemplateV1, TemplateV2 } from '../../common';
+import { TemplateV1Serialized, TemplateV2Serialized } from '../types';
import { getTemplateVersion } from './utils';
describe('utils', () => {
describe('getTemplateVersion', () => {
test('should detect v1 template', () => {
const template = {
- indexPatterns: ['logs*'],
+ name: 'my_template',
+ index_patterns: ['logs*'],
mappings: {
properties: {},
},
};
- expect(getTemplateVersion(template as TemplateV1)).toBe(1);
+ expect(getTemplateVersion(template as TemplateV1Serialized)).toBe(1);
});
test('should detect v2 template', () => {
const template = {
- indexPatterns: ['logs*'],
+ name: 'my_template',
+ index_patterns: ['logs*'],
template: {
mappings: {
properties: {},
},
},
};
- expect(getTemplateVersion(template as TemplateV2)).toBe(2);
+ expect(getTemplateVersion(template as TemplateV2Serialized)).toBe(2);
});
});
});
diff --git a/x-pack/plugins/index_management/server/lib/utils.ts b/x-pack/plugins/index_management/common/lib/utils.ts
similarity index 68%
rename from x-pack/plugins/index_management/server/lib/utils.ts
rename to x-pack/plugins/index_management/common/lib/utils.ts
index 2fac96d2e05b5..20444dd03c704 100644
--- a/x-pack/plugins/index_management/server/lib/utils.ts
+++ b/x-pack/plugins/index_management/common/lib/utils.ts
@@ -4,13 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { TemplateDeserialized } from '../../common';
+import { TemplateDeserialized, TemplateV1Serialized, TemplateV2Serialized } from '../types';
/**
* Helper to get the format version of an index template.
* v1 will be supported up until 9.x but marked as deprecated from 7.8
* v2 will be supported from 7.8
*/
-export const getTemplateVersion = (template: TemplateDeserialized): 1 | 2 => {
+export const getTemplateVersion = (
+ template: TemplateDeserialized | TemplateV1Serialized | TemplateV2Serialized
+): 1 | 2 => {
return {}.hasOwnProperty.call(template, 'template') ? 2 : 1;
};
From ac46764d600515bcc123fd1cce6cfe6757eb5ea5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Wed, 25 Mar 2020 16:55:42 +0100
Subject: [PATCH 05/31] Differentiate serialisation to V1 or V2 template format
---
.../common/lib/template_serialization.ts | 39 ++++++++++++-------
.../template_form/steps/step_review.tsx | 6 ++-
.../api/templates/register_create_route.ts | 6 +--
.../api/templates/register_get_routes.ts | 4 +-
.../api/templates/register_update_route.ts | 6 +--
5 files changed, 38 insertions(+), 23 deletions(-)
diff --git a/x-pack/plugins/index_management/common/lib/template_serialization.ts b/x-pack/plugins/index_management/common/lib/template_serialization.ts
index 1e07eb4a1ebaa..3ae64a0a1d906 100644
--- a/x-pack/plugins/index_management/common/lib/template_serialization.ts
+++ b/x-pack/plugins/index_management/common/lib/template_serialization.ts
@@ -3,14 +3,21 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import { TemplateDeserialized, TemplateSerialized, TemplateListItem } from '../types';
+import { TemplateDeserialized, TemplateV1Serialized, TemplateListItem } from '../types';
+import { getTemplateVersion } from './utils';
const hasEntries = (data: object = {}) => Object.entries(data).length > 0;
-export function serializeTemplate(template: TemplateDeserialized): TemplateSerialized {
- const { name, version, order, indexPatterns, settings, aliases, mappings } = template;
+export function serializeV1Template(template: TemplateDeserialized): TemplateV1Serialized {
+ const {
+ name,
+ version,
+ order,
+ indexPatterns,
+ template: { settings, aliases, mappings },
+ } = template;
- const serializedTemplate: TemplateSerialized = {
+ const serializedTemplate: TemplateV1Serialized = {
name,
version,
order,
@@ -23,8 +30,8 @@ export function serializeTemplate(template: TemplateDeserialized): TemplateSeria
return serializedTemplate;
}
-export function deserializeTemplate(
- templateEs: TemplateSerialized,
+export function deserializeV1Template(
+ templateEs: TemplateV1Serialized,
managedTemplatePrefix?: string
): TemplateDeserialized {
const {
@@ -42,24 +49,30 @@ export function deserializeTemplate(
version,
order,
indexPatterns: indexPatterns.sort(),
- settings,
- aliases,
- mappings,
+ template: {
+ settings,
+ aliases,
+ mappings,
+ },
ilmPolicy: settings && settings.index && settings.index.lifecycle,
isManaged: Boolean(managedTemplatePrefix && name.startsWith(managedTemplatePrefix)),
+ _kbnMeta: {
+ formatVersion: getTemplateVersion(templateEs),
+ },
};
return deserializedTemplate;
}
export function deserializeTemplateList(
- indexTemplatesByName: { [key: string]: TemplateSerialized },
+ indexTemplatesByName: { [key: string]: TemplateV1Serialized },
managedTemplatePrefix?: string
): TemplateListItem[] {
return Object.values(indexTemplatesByName).map(templateSerialized => {
- const { mappings, settings, aliases, ...deserializedTemplate } = deserializeTemplate(
- templateSerialized
- );
+ const {
+ template: { mappings, settings, aliases },
+ ...deserializedTemplate
+ } = deserializeV1Template(templateSerialized);
return {
...deserializedTemplate,
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
index 9cc2bb436f5a9..0235a1ba651bb 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
@@ -22,7 +22,7 @@ import {
import { FormattedMessage } from '@kbn/i18n/react';
import { serializers } from '../../../../shared_imports';
-import { serializeTemplate } from '../../../../../common/lib/template_serialization';
+import { serializeV1Template } from '../../../../../common/lib/template_serialization';
import { TemplateDeserialized } from '../../../../../common';
import { StepProps } from '../types';
@@ -54,7 +54,9 @@ const getDescriptionText = (data: any) => {
export const StepReview: React.FunctionComponent = ({ template, updateCurrentStep }) => {
const { name, indexPatterns, version, order } = template;
- const serializedTemplate = serializeTemplate(stripEmptyFields(template) as TemplateDeserialized);
+ const serializedTemplate = serializeV1Template(
+ stripEmptyFields(template) as TemplateDeserialized
+ );
// Name not included in ES request body
delete serializedTemplate.name;
const {
diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts
index afb4e910847dd..e22246c7f2b49 100644
--- a/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts
+++ b/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts
@@ -6,8 +6,8 @@
import { i18n } from '@kbn/i18n';
-import { TemplateDeserialized, TemplateSerialized } from '../../../../common';
-import { serializeTemplate } from '../../../../common/lib';
+import { TemplateDeserialized } from '../../../../common';
+import { serializeV1Template } from '../../../../common/lib';
import { RouteDependencies } from '../../../types';
import { addBasePath } from '../index';
import { templateSchema } from './validate_schemas';
@@ -20,7 +20,7 @@ export function registerCreateRoute({ router, license, lib }: RouteDependencies)
license.guardApiRoute(async (ctx, req, res) => {
const { callAsCurrentUser } = ctx.core.elasticsearch.dataClient;
const template = req.body as TemplateDeserialized;
- const serializedTemplate = serializeTemplate(template) as TemplateSerialized;
+ const serializedTemplate = serializeV1Template(template);
const {
name,
diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts
index d6776d774d3bf..456ebe8f72cb1 100644
--- a/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts
+++ b/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts
@@ -5,7 +5,7 @@
*/
import { schema } from '@kbn/config-schema';
-import { deserializeTemplate, deserializeTemplateList } from '../../../../common/lib';
+import { deserializeV1Template, deserializeTemplateList } from '../../../../common/lib';
import { getManagedTemplatePrefix } from '../../../lib/get_managed_templates';
import { RouteDependencies } from '../../../types';
import { addBasePath } from '../index';
@@ -41,7 +41,7 @@ export function registerGetOneRoute({ router, license, lib }: RouteDependencies)
if (indexTemplateByName[name]) {
return res.ok({
- body: deserializeTemplate(
+ body: deserializeV1Template(
{ ...indexTemplateByName[name], name },
managedTemplatePrefix
),
diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts
index cfcaf19f9c9bf..e0b5085fa3914 100644
--- a/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts
+++ b/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts
@@ -5,8 +5,8 @@
*/
import { schema } from '@kbn/config-schema';
-import { TemplateDeserialized, TemplateSerialized } from '../../../../common';
-import { serializeTemplate } from '../../../../common/lib';
+import { TemplateDeserialized } from '../../../../common';
+import { serializeV1Template } from '../../../../common/lib';
import { RouteDependencies } from '../../../types';
import { addBasePath } from '../index';
import { templateSchema } from './validate_schemas';
@@ -26,7 +26,7 @@ export function registerUpdateRoute({ router, license, lib }: RouteDependencies)
const { callAsCurrentUser } = ctx.core.elasticsearch.dataClient;
const { name } = req.params as typeof paramsSchema.type;
const template = req.body as TemplateDeserialized;
- const serializedTemplate = serializeTemplate(template) as TemplateSerialized;
+ const serializedTemplate = serializeV1Template(template);
const { order, index_patterns, version, settings, mappings, aliases } = serializedTemplate;
From 0054cb00884d2143d417162616504026f7423657 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Wed, 25 Mar 2020 16:56:25 +0100
Subject: [PATCH 06/31] Improve typings and make V2 format the default for
client
---
.../index_management/common/lib/index.ts | 4 +-
.../index_management/common/types/aliases.ts | 9 ++
.../index_management/common/types/index.ts | 6 ++
.../index_management/common/types/indices.ts | 43 +++++++++
.../index_management/common/types/mappings.ts | 11 +++
.../common/types/templates.ts | 94 ++++++++++++-------
6 files changed, 129 insertions(+), 38 deletions(-)
create mode 100644 x-pack/plugins/index_management/common/types/aliases.ts
create mode 100644 x-pack/plugins/index_management/common/types/indices.ts
create mode 100644 x-pack/plugins/index_management/common/types/mappings.ts
diff --git a/x-pack/plugins/index_management/common/lib/index.ts b/x-pack/plugins/index_management/common/lib/index.ts
index 83b22d8d72e92..5270740edaed0 100644
--- a/x-pack/plugins/index_management/common/lib/index.ts
+++ b/x-pack/plugins/index_management/common/lib/index.ts
@@ -5,6 +5,6 @@
*/
export {
deserializeTemplateList,
- deserializeTemplate,
- serializeTemplate,
+ deserializeV1Template,
+ serializeV1Template,
} from './template_serialization';
diff --git a/x-pack/plugins/index_management/common/types/aliases.ts b/x-pack/plugins/index_management/common/types/aliases.ts
new file mode 100644
index 0000000000000..76aae8585c065
--- /dev/null
+++ b/x-pack/plugins/index_management/common/types/aliases.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export interface Aliases {
+ [key: string]: any;
+}
diff --git a/x-pack/plugins/index_management/common/types/index.ts b/x-pack/plugins/index_management/common/types/index.ts
index 922cb63b70da3..b467f020978a5 100644
--- a/x-pack/plugins/index_management/common/types/index.ts
+++ b/x-pack/plugins/index_management/common/types/index.ts
@@ -4,4 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
+export * from './aliases';
+
+export * from './indices';
+
+export * from './mappings';
+
export * from './templates';
diff --git a/x-pack/plugins/index_management/common/types/indices.ts b/x-pack/plugins/index_management/common/types/indices.ts
new file mode 100644
index 0000000000000..84421c4effad5
--- /dev/null
+++ b/x-pack/plugins/index_management/common/types/indices.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+interface IndexModule {
+ number_of_shards: number;
+ codec: string;
+ routing_partition_size: number;
+ load_fixed_bitset_filters_eagerly: boolean;
+ shard: {
+ check_on_startup: boolean | 'checksum';
+ };
+ number_of_replicas: number;
+ auto_expand_replicas: false | string;
+ lifecycle: LifecycleModule;
+}
+
+interface AnalysisModule {
+ analyzer: {
+ [key: string]: {
+ type: string;
+ tokenizer: string;
+ char_filter?: string[];
+ filter?: string[];
+ position_increment_gap?: number;
+ };
+ };
+}
+
+interface LifecycleModule {
+ name: string;
+ rollover_alias?: string;
+ parse_origination_date?: boolean;
+ origination_date?: number;
+}
+
+export interface IndexSettings {
+ index?: Partial;
+ analysis?: AnalysisModule;
+ [key: string]: any;
+}
diff --git a/x-pack/plugins/index_management/common/types/mappings.ts b/x-pack/plugins/index_management/common/types/mappings.ts
new file mode 100644
index 0000000000000..0bd3e38ed07a5
--- /dev/null
+++ b/x-pack/plugins/index_management/common/types/mappings.ts
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+// TODO: Move mappings type from Mappings editor here
+
+export interface Mappings {
+ [key: string]: any;
+}
diff --git a/x-pack/plugins/index_management/common/types/templates.ts b/x-pack/plugins/index_management/common/types/templates.ts
index 14ec8ddaff56b..2d258f096710e 100644
--- a/x-pack/plugins/index_management/common/types/templates.ts
+++ b/x-pack/plugins/index_management/common/types/templates.ts
@@ -4,20 +4,38 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export interface TemplateListItem {
+import { IndexSettings } from './indices';
+import { Aliases } from './aliases';
+import { Mappings } from './mappings';
+
+// Template serialized (from Elasticsearch)
+interface TemplateBaseSerialized {
name: string;
- indexPatterns: string[];
+ index_patterns: string[];
version?: number;
order?: number;
- hasSettings: boolean;
- hasAliases: boolean;
- hasMappings: boolean;
- ilmPolicy?: {
- name: string;
+}
+
+export interface TemplateV1Serialized extends TemplateBaseSerialized {
+ settings?: IndexSettings;
+ aliases?: Aliases;
+ mappings?: Mappings;
+}
+
+export interface TemplateV2Serialized {
+ name: string;
+ index_patterns: string[];
+ version?: number;
+ order?: number;
+ template: {
+ settings?: IndexSettings;
+ aliases?: Aliases;
+ mappings?: Mappings;
};
- isManaged: boolean;
}
-interface TemplateBase {
+
+// Template Deserialized
+interface TemplateBaseDeserialized {
name: string;
indexPatterns: string[];
version?: number;
@@ -28,42 +46,46 @@ interface TemplateBase {
isManaged: boolean;
}
-export interface TemplateV1 extends TemplateBase {
- settings?: object;
- aliases?: object;
- mappings?: object;
+export interface TemplateV1Deserialized extends TemplateBaseDeserialized {
+ settings?: IndexSettings;
+ aliases?: Aliases;
+ mappings?: Mappings;
}
-export interface TemplateV2 extends TemplateBase {
+export interface TemplateV2Deserialized extends TemplateBaseDeserialized {
template: {
- settings?: object;
- aliases?: object;
- mappings?: object;
- };
-}
-
-export interface TemplateDeserialized extends TemplateV1 {
- _kbnMeta?: {
- version: 1 | 2;
+ settings?: IndexSettings;
+ aliases?: Aliases;
+ mappings?: Mappings;
};
}
-export interface TemplateSerialized {
+/**
+ * Interface for the template list in our UI table
+ * we don't include the mappings, settings and aliases
+ * to reduce the payload size sent back to the client.
+ */
+export interface TemplateListItem {
name: string;
- index_patterns: string[];
+ indexPatterns: string[];
version?: number;
order?: number;
- settings?: {
- [key: string]: any;
- index?: {
- [key: string]: any;
- lifecycle?: {
- name: string;
- };
- };
+ hasSettings: boolean;
+ hasAliases: boolean;
+ hasMappings: boolean;
+ ilmPolicy?: {
+ name: string;
};
- aliases?: {
- [key: string]: any;
+ isManaged: boolean;
+}
+
+/**
+ * TemplateDeserialized falls back to index template V2 format
+ * The UI will only be dealing with this interface, conversion from and to V1 format
+ * is done server side.
+ */
+export interface TemplateDeserialized extends TemplateV2Deserialized {
+ _kbnMeta: {
+ formatVersion: 1 | 2;
};
- mappings?: object;
}
From 570e61f89f22d1d2e2eb9f0bed81497d1796200d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Thu, 26 Mar 2020 14:28:36 +0100
Subject: [PATCH 07/31] Fix api integration tests
---
.../common/lib/template_serialization.ts | 6 +--
.../api/templates/register_get_routes.ts | 3 +-
.../routes/api/templates/validate_schemas.ts | 13 ++++--
.../index_management/templates.helpers.js | 43 +++++++++++--------
.../management/index_management/templates.js | 27 ++++++++----
5 files changed, 57 insertions(+), 35 deletions(-)
diff --git a/x-pack/plugins/index_management/common/lib/template_serialization.ts b/x-pack/plugins/index_management/common/lib/template_serialization.ts
index 3ae64a0a1d906..7b4497b817f20 100644
--- a/x-pack/plugins/index_management/common/lib/template_serialization.ts
+++ b/x-pack/plugins/index_management/common/lib/template_serialization.ts
@@ -65,14 +65,14 @@ export function deserializeV1Template(
}
export function deserializeTemplateList(
- indexTemplatesByName: { [key: string]: TemplateV1Serialized },
+ indexTemplatesByName: { [key: string]: Omit },
managedTemplatePrefix?: string
): TemplateListItem[] {
- return Object.values(indexTemplatesByName).map(templateSerialized => {
+ return Object.entries(indexTemplatesByName).map(([name, templateSerialized]) => {
const {
template: { mappings, settings, aliases },
...deserializedTemplate
- } = deserializeV1Template(templateSerialized);
+ } = deserializeV1Template({ name, ...templateSerialized }, managedTemplatePrefix);
return {
...deserializedTemplate,
diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts
index 456ebe8f72cb1..d4a56f87fb508 100644
--- a/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts
+++ b/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts
@@ -18,8 +18,9 @@ export function registerGetAllRoute({ router, license }: RouteDependencies) {
const managedTemplatePrefix = await getManagedTemplatePrefix(callAsCurrentUser);
const indexTemplatesByName = await callAsCurrentUser('indices.getTemplate');
+ const body = deserializeTemplateList(indexTemplatesByName, managedTemplatePrefix);
- return res.ok({ body: deserializeTemplateList(indexTemplatesByName, managedTemplatePrefix) });
+ return res.ok({ body });
})
);
}
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 8bf2774ac38b3..491a686f81177 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
@@ -11,9 +11,13 @@ export const templateSchema = schema.object({
indexPatterns: schema.arrayOf(schema.string()),
version: schema.maybe(schema.number()),
order: schema.maybe(schema.number()),
- settings: schema.maybe(schema.object({}, { unknowns: 'allow' })),
- aliases: schema.maybe(schema.object({}, { unknowns: 'allow' })),
- mappings: schema.maybe(schema.object({}, { unknowns: 'allow' })),
+ template: schema.maybe(
+ schema.object({
+ settings: schema.maybe(schema.object({}, { unknowns: 'allow' })),
+ aliases: schema.maybe(schema.object({}, { unknowns: 'allow' })),
+ mappings: schema.maybe(schema.object({}, { unknowns: 'allow' })),
+ })
+ ),
ilmPolicy: schema.maybe(
schema.object({
name: schema.maybe(schema.string()),
@@ -21,4 +25,7 @@ export const templateSchema = schema.object({
})
),
isManaged: schema.maybe(schema.boolean()),
+ _kbnMeta: schema.object({
+ formatVersion: schema.oneOf([schema.literal(1), schema.literal(2)]),
+ }),
});
diff --git a/x-pack/test/api_integration/apis/management/index_management/templates.helpers.js b/x-pack/test/api_integration/apis/management/index_management/templates.helpers.js
index d409d66e3459c..339fedc193c7a 100644
--- a/x-pack/test/api_integration/apis/management/index_management/templates.helpers.js
+++ b/x-pack/test/api_integration/apis/management/index_management/templates.helpers.js
@@ -11,35 +11,40 @@ export const registerHelpers = ({ supertest }) => {
const getOneTemplate = name => supertest.get(`${API_BASE_PATH}/templates/${name}`);
- const getTemplatePayload = name => ({
+ const getTemplatePayload = (name, formatVersion = 1) => ({
name,
order: 1,
indexPatterns: INDEX_PATTERNS,
version: 1,
- settings: {
- number_of_shards: 1,
- index: {
- lifecycle: {
- name: 'my_policy',
+ template: {
+ settings: {
+ number_of_shards: 1,
+ index: {
+ lifecycle: {
+ name: 'my_policy',
+ },
},
},
- },
- mappings: {
- _source: {
- enabled: false,
- },
- properties: {
- host_name: {
- type: 'keyword',
+ mappings: {
+ _source: {
+ enabled: false,
},
- created_at: {
- type: 'date',
- format: 'EEE MMM dd HH:mm:ss Z yyyy',
+ properties: {
+ host_name: {
+ type: 'keyword',
+ },
+ created_at: {
+ type: 'date',
+ format: 'EEE MMM dd HH:mm:ss Z yyyy',
+ },
},
},
+ aliases: {
+ alias1: {},
+ },
},
- aliases: {
- alias1: {},
+ _kbnMeta: {
+ formatVersion,
},
});
diff --git a/x-pack/test/api_integration/apis/management/index_management/templates.js b/x-pack/test/api_integration/apis/management/index_management/templates.js
index d9344846ebb91..939a395b3ced0 100644
--- a/x-pack/test/api_integration/apis/management/index_management/templates.js
+++ b/x-pack/test/api_integration/apis/management/index_management/templates.js
@@ -35,7 +35,7 @@ export default function({ getService }) {
await createTemplate(payload).expect(200);
});
- it('should list all the index templates with the expected properties', async () => {
+ it('should list all the index templates with the expected parameters', async () => {
const { body: templates } = await getAllTemplates().expect(200);
const createdTemplate = templates.find(template => template.name === payload.name);
@@ -46,8 +46,13 @@ export default function({ getService }) {
'hasAliases',
'hasMappings',
'ilmPolicy',
- ];
- expectedKeys.forEach(key => expect(Object.keys(createdTemplate).includes(key)).to.be(true));
+ 'isManaged',
+ 'order',
+ 'version',
+ '_kbnMeta',
+ ].sort();
+
+ expect(Object.keys(createdTemplate).sort()).to.eql(expectedKeys);
});
});
@@ -59,19 +64,23 @@ export default function({ getService }) {
await createTemplate(payload).expect(200);
});
- it('should list the index template with the expected properties', async () => {
+ it('should return the index template with the expected parameters', async () => {
const { body } = await getOneTemplate(templateName).expect(200);
const expectedKeys = [
'name',
'indexPatterns',
- 'settings',
- 'aliases',
- 'mappings',
+ 'template',
'ilmPolicy',
- ];
+ 'isManaged',
+ 'order',
+ 'version',
+ '_kbnMeta',
+ ].sort();
+ const expectedTemplateKeys = ['aliases', 'mappings', 'settings'].sort();
expect(body.name).to.equal(templateName);
- expectedKeys.forEach(key => expect(Object.keys(body).includes(key)).to.be(true));
+ expect(Object.keys(body).sort()).to.eql(expectedKeys);
+ expect(Object.keys(body.template).sort()).to.eql(expectedTemplateKeys);
});
});
From 4763cefa94f350570d5f8c2608107e931ce7da50 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Thu, 26 Mar 2020 16:00:33 +0100
Subject: [PATCH 08/31] Refactor client code to work with V2 template format
(in and out)
---
.../components/mappings_editor/reducer.ts | 16 +++++++--
.../template_form/steps/step_aliases.tsx | 2 +-
.../template_form/steps/step_mappings.tsx | 19 ++++++----
.../template_form/steps/step_settings.tsx | 2 +-
.../template_form/steps/use_json_step.ts | 6 ++--
.../template_form/template_form.tsx | 36 +++++++++++++------
.../components/template_form/types.ts | 11 ++++--
.../template_details/tabs/tab_aliases.tsx | 4 ++-
.../template_details/tabs/tab_mappings.tsx | 4 ++-
.../template_details/tabs/tab_settings.tsx | 4 ++-
.../template_details/tabs/tab_summary.tsx | 7 ++++
.../template_details/template_details.tsx | 3 +-
.../home/template_list/template_list.tsx | 5 +--
.../template_table/template_table.tsx | 12 +++----
.../public/application/services/api.ts | 6 ++--
.../application/services/use_request.ts | 4 +--
16 files changed, 95 insertions(+), 46 deletions(-)
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts
index a799f9ddcbc2b..2f30363c1ff58 100644
--- a/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts
@@ -33,11 +33,21 @@ export interface MappingsConfiguration {
}
export interface MappingsTemplates {
- dynamic_templates: TemplateDeserialized[];
+ dynamic_templates: DynamicTemplate[];
}
-interface TemplateDeserialized {
- [key: string]: any;
+interface DynamicTemplate {
+ [key: string]: {
+ mapping: {
+ [key: string]: any;
+ };
+ match_mapping_type?: string;
+ match?: string;
+ unmatch?: string;
+ match_pattern?: string;
+ path_match?: string;
+ path_unmatch?: string;
+ };
}
export interface MappingsFields {
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx
index 8628b6d8b8d74..50a32787c7a04 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx
@@ -29,7 +29,7 @@ export const StepAliases: React.FunctionComponent = ({
}) => {
const { content, setContent, error } = useJsonStep({
prop: 'aliases',
- defaultValue: template.aliases,
+ defaultValue: template?.template.aliases,
setDataGetter,
onStepValidityChange,
});
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx
index d51d512429ea4..cf9b57dcbcb14 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx
@@ -15,7 +15,7 @@ import {
EuiText,
} from '@elastic/eui';
import { documentationService } from '../../../services/documentation';
-import { StepProps } from '../types';
+import { StepProps, DataGetterFunc } from '../types';
import { MappingsEditor, OnUpdateHandler, LoadMappingsFromJsonButton } from '../../mappings_editor';
export const StepMappings: React.FunctionComponent = ({
@@ -23,16 +23,23 @@ export const StepMappings: React.FunctionComponent = ({
setDataGetter,
onStepValidityChange,
}) => {
- const [mappings, setMappings] = useState(template.mappings);
+ const [mappings, setMappings] = useState(template?.template.mappings);
const onMappingsEditorUpdate = useCallback(
({ isValid, getData, validate }) => {
onStepValidityChange(isValid);
- setDataGetter(async () => {
+
+ const dataGetterFunc: DataGetterFunc = async () => {
const isMappingsValid = isValid === undefined ? await validate() : isValid;
const data = getData(isMappingsValid);
- return Promise.resolve({ isValid: isMappingsValid, data: { mappings: data } });
- });
+ return {
+ isValid: isMappingsValid,
+ data: { mappings: data },
+ path: 'template',
+ };
+ };
+
+ setDataGetter(dataGetterFunc);
},
[setDataGetter, onStepValidityChange]
);
@@ -96,7 +103,7 @@ export const StepMappings: React.FunctionComponent = ({
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx
index cead652c9f6fc..7c1ee6388a618 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx
@@ -29,7 +29,7 @@ export const StepSettings: React.FunctionComponent = ({
}) => {
const { content, setContent, error } = useJsonStep({
prop: 'settings',
- defaultValue: template.settings,
+ defaultValue: template?.template.settings,
setDataGetter,
onStepValidityChange,
});
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts b/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts
index fbe479ea0cf23..54b18e27365f5 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts
+++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts
@@ -8,7 +8,7 @@ import { useEffect, useState, useCallback } from 'react';
import { i18n } from '@kbn/i18n';
import { isJSON } from '../../../../shared_imports';
-import { StepProps } from '../types';
+import { StepProps, DataGetterFunc } from '../types';
interface Parameters {
prop: 'settings' | 'mappings' | 'aliases';
@@ -44,11 +44,11 @@ export const useJsonStep = ({
return isValid;
}, [content]);
- const dataGetter = useCallback(() => {
+ const dataGetter = useCallback(() => {
const isValid = validateContent();
const value = isValid && content.trim() !== '' ? JSON.parse(content) : {};
const data = { [prop]: value };
- return Promise.resolve({ isValid, data });
+ return Promise.resolve({ isValid, data, path: 'template' });
}, [content, validateContent, prop]);
useEffect(() => {
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
index f6ef5961091c3..97a79e56fb4e6 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
@@ -47,7 +47,16 @@ const stepComponentMap: { [key: number]: React.FunctionComponent } =
};
export const TemplateForm: React.FunctionComponent = ({
- defaultValue = { isManaged: false },
+ defaultValue = {
+ name: 'untitled',
+ indexPatterns: [],
+ template: {},
+ isManaged: false,
+ _kbnMeta: {
+ // When the V2 API will be ready, we will create V2 template format
+ formatVersion: 1,
+ },
+ },
onSave,
isSaving,
saveError,
@@ -63,7 +72,7 @@ export const TemplateForm: React.FunctionComponent = ({
5: defaultValidation,
});
- const template = useRef>(defaultValue);
+ const template = useRef(defaultValue);
const stepsDataGetters = useRef>({});
const lastStep = Object.keys(stepComponentMap).length;
@@ -91,17 +100,24 @@ export const TemplateForm: React.FunctionComponent = ({
);
const validateAndGetDataFromCurrentStep = async () => {
- const validateAndGetData = stepsDataGetters.current[currentStep];
+ const validateAndGetStepData = stepsDataGetters.current[currentStep];
- if (!validateAndGetData) {
+ if (!validateAndGetStepData) {
throw new Error(`No data getter has been set for step "${currentStep}"`);
}
- const { isValid, data } = await validateAndGetData();
+ const { isValid, data, path } = await validateAndGetStepData();
if (isValid) {
- // Update the template object
- template.current = { ...template.current, ...data };
+ // Update the template object with the current step data
+ if (path) {
+ template.current = {
+ ...template.current,
+ [path]: { ...template.current[path as 'template'], ...data },
+ };
+ } else {
+ template.current = { ...template.current, ...data };
+ }
}
return { isValid, data };
@@ -111,9 +127,9 @@ export const TemplateForm: React.FunctionComponent = ({
// All steps needs validation, except for the last step
const shouldValidate = currentStep !== lastStep;
- let isValid = isStepValid;
if (shouldValidate) {
- isValid = isValid === false ? false : (await validateAndGetDataFromCurrentStep()).isValid;
+ const isValid =
+ isStepValid === false ? false : (await validateAndGetDataFromCurrentStep()).isValid;
// If step is invalid do not let user proceed
if (!isValid) {
@@ -224,7 +240,7 @@ export const TemplateForm: React.FunctionComponent = ({
iconType="check"
onClick={onSave.bind(
null,
- stripEmptyFields(template.current) as TemplateDeserialized
+ stripEmptyFields(template.current!) as TemplateDeserialized
)}
data-test-subj="submitButton"
isLoading={isSaving}
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/types.ts b/x-pack/plugins/index_management/public/application/components/template_form/types.ts
index edeb67b2ada18..5db53e91ed261 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/types.ts
+++ b/x-pack/plugins/index_management/public/application/components/template_form/types.ts
@@ -7,11 +7,18 @@
import { TemplateDeserialized } from '../../../../common';
export interface StepProps {
- template: Partial;
+ template?: TemplateDeserialized;
setDataGetter: (dataGetter: DataGetterFunc) => void;
updateCurrentStep: (step: number) => void;
onStepValidityChange: (isValid: boolean | undefined) => void;
isEditing?: boolean;
}
-export type DataGetterFunc = () => Promise<{ isValid: boolean; data: any }>;
+export type DataGetterFunc = () => Promise<{
+ /** Is the step data valid or not */
+ isValid: boolean;
+ /** The current step data (can be invalid) */
+ data: any;
+ /** Optional "slice" of the complete object the step is updating */
+ path?: string;
+}>;
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx
index 0dd1b78529724..fa7d734ad0d2b 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx
@@ -14,7 +14,9 @@ interface Props {
}
export const TabAliases: React.FunctionComponent = ({ templateDetails }) => {
- const { aliases } = templateDetails;
+ const {
+ template: { aliases },
+ } = templateDetails;
if (aliases && Object.keys(aliases).length) {
return (
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx
index 3e07ced74a251..6e0257c6b377b 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx
@@ -14,7 +14,9 @@ interface Props {
}
export const TabMappings: React.FunctionComponent = ({ templateDetails }) => {
- const { mappings } = templateDetails;
+ const {
+ template: { mappings },
+ } = templateDetails;
if (mappings && Object.keys(mappings).length) {
return (
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx
index f029a7db3c39a..8f75c2cb77801 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx
@@ -14,7 +14,9 @@ interface Props {
}
export const TabSettings: React.FunctionComponent = ({ templateDetails }) => {
- const { settings } = templateDetails;
+ const {
+ template: { settings },
+ } = templateDetails;
if (settings && Object.keys(settings).length) {
return (
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx
index 0ee2dad6aef72..9ce29ab746a2f 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx
@@ -35,6 +35,7 @@ export const TabSummary: React.FunctionComponent = ({ templateDetails })
return (
+ {/* Index patterns */}
= ({ templateDetails })
indexPatterns.toString()
)}
+
+ {/* // ILM Policy */}
= ({ templateDetails })
)}
+
+ {/* // Order */}
= ({ templateDetails })
{order || order === 0 ? order : }
+
+ {/* // Version */}
= ({
const { uiMetricService } = useServices();
const decodedTemplateName = decodePath(templateName);
const { error, data: templateDetails, isLoading } = useLoadIndexTemplate(decodedTemplateName);
- // TS complains if we use destructuring here. Fixed in 3.6.0 (https://github.com/microsoft/TypeScript/pull/31711).
- const isManaged = templateDetails ? templateDetails.isManaged : undefined;
+ const isManaged = templateDetails?.isManaged;
const [templateToDelete, setTemplateToDelete] = useState>([]);
const [activeTab, setActiveTab] = useState(SUMMARY_TAB_ID);
const [isPopoverOpen, setIsPopOverOpen] = useState(false);
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
index ca44fd5e9d868..53f4913c4bb8e 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
@@ -48,10 +48,7 @@ export const TemplateList: React.FunctionComponent
- templates
- ? templates.filter((template: TemplateDeserialized) => !template.name.startsWith('.'))
- : [],
+ () => (templates !== null ? templates.filter(template => !template.name.startsWith('.')) : []),
[templates]
);
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx
index d895d8a9e86a5..33ff692bc26eb 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx
@@ -133,10 +133,10 @@ export const TemplateTable: React.FunctionComponent = ({
}),
icon: 'pencil',
type: 'icon',
- onClick: ({ name }: TemplateDeserialized) => {
+ onClick: ({ name }: TemplateListItem) => {
editTemplate(name);
},
- enabled: ({ isManaged }: TemplateDeserialized) => !isManaged,
+ enabled: ({ isManaged }: TemplateListItem) => !isManaged,
},
{
type: 'icon',
@@ -147,7 +147,7 @@ export const TemplateTable: React.FunctionComponent = ({
defaultMessage: 'Clone this template',
}),
icon: 'copy',
- onClick: ({ name }: TemplateDeserialized) => {
+ onClick: ({ name }: TemplateListItem) => {
cloneTemplate(name);
},
},
@@ -161,11 +161,11 @@ export const TemplateTable: React.FunctionComponent = ({
icon: 'trash',
color: 'danger',
type: 'icon',
- onClick: ({ name }: TemplateDeserialized) => {
+ onClick: ({ name }: TemplateListItem) => {
setTemplatesToDelete([name]);
},
isPrimary: true,
- enabled: ({ isManaged }: TemplateDeserialized) => !isManaged,
+ enabled: ({ isManaged }: TemplateListItem) => !isManaged,
},
],
},
@@ -185,7 +185,7 @@ export const TemplateTable: React.FunctionComponent = ({
const selectionConfig = {
onSelectionChange: setSelection,
- selectable: ({ isManaged }: TemplateDeserialized) => !isManaged,
+ selectable: ({ isManaged }: TemplateListItem) => !isManaged,
selectableMessage: (selectable: boolean) => {
if (!selectable) {
return i18n.translate('xpack.idxMgmt.templateList.table.deleteManagedTemplateTooltip', {
diff --git a/x-pack/plugins/index_management/public/application/services/api.ts b/x-pack/plugins/index_management/public/application/services/api.ts
index 2c2b65a108bab..82944cc3db5bf 100644
--- a/x-pack/plugins/index_management/public/application/services/api.ts
+++ b/x-pack/plugins/index_management/public/application/services/api.ts
@@ -37,7 +37,7 @@ import { TAB_SETTINGS, TAB_MAPPING, TAB_STATS } from '../constants';
import { useRequest, sendRequest } from './use_request';
import { httpService } from './http';
import { UiMetricService } from './ui_metric';
-import { TemplateDeserialized } from '../../../common';
+import { TemplateDeserialized, TemplateListItem } from '../../../common';
import { IndexMgmtMetricsType } from '../../types';
// Temporary hack to provide the uiMetricService instance to this file.
@@ -201,7 +201,7 @@ export async function loadIndexData(type: string, indexName: string) {
}
export function useLoadIndexTemplates() {
- return useRequest({
+ return useRequest({
path: `${API_BASE_PATH}/templates`,
method: 'get',
});
@@ -221,7 +221,7 @@ export async function deleteTemplates(names: Array
}
export function useLoadIndexTemplate(name: TemplateDeserialized['name']) {
- return useRequest({
+ return useRequest({
path: `${API_BASE_PATH}/templates/${encodeURIComponent(name)}`,
method: 'get',
});
diff --git a/x-pack/plugins/index_management/public/application/services/use_request.ts b/x-pack/plugins/index_management/public/application/services/use_request.ts
index 8ede3d196911c..b87a6db20f0bf 100644
--- a/x-pack/plugins/index_management/public/application/services/use_request.ts
+++ b/x-pack/plugins/index_management/public/application/services/use_request.ts
@@ -18,6 +18,6 @@ export const sendRequest = (config: SendRequestConfig): Promise {
- return _useRequest(httpService.httpClient, config);
+export const useRequest = (config: UseRequestConfig) => {
+ return _useRequest(httpService.httpClient, config);
};
From 20d24b74bb93bbb6a27f875e5ae3daf2db782a34 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Fri, 27 Mar 2020 12:29:13 +0100
Subject: [PATCH 09/31] [useRequest] Fix TS type for data returned
---
.../public/request/np_ready_request.ts | 2 +-
.../__jest__/client_integration/home.test.ts | 44 ++++++++++---------
2 files changed, 25 insertions(+), 21 deletions(-)
diff --git a/src/plugins/es_ui_shared/public/request/np_ready_request.ts b/src/plugins/es_ui_shared/public/request/np_ready_request.ts
index 6771abd64df7e..06af698f2ce02 100644
--- a/src/plugins/es_ui_shared/public/request/np_ready_request.ts
+++ b/src/plugins/es_ui_shared/public/request/np_ready_request.ts
@@ -43,7 +43,7 @@ export interface UseRequestResponse {
isInitialRequest: boolean;
isLoading: boolean;
error: E | null;
- data: D | null;
+ data?: D | null;
sendRequest: (...args: any[]) => Promise>;
}
diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts
index 9e8af02b74631..440d8213867c8 100644
--- a/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts
+++ b/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts
@@ -115,11 +115,13 @@ describe('', () => {
const template1 = fixtures.getTemplate({
name: `a${getRandomString()}`,
indexPatterns: ['template1Pattern1*', 'template1Pattern2'],
- settings: {
- index: {
- number_of_shards: '1',
- lifecycle: {
- name: 'my_ilm_policy',
+ template: {
+ settings: {
+ index: {
+ number_of_shards: '1',
+ lifecycle: {
+ name: 'my_ilm_policy',
+ },
},
},
},
@@ -396,24 +398,26 @@ describe('', () => {
const template = fixtures.getTemplate({
name: `a${getRandomString()}`,
indexPatterns: ['template1Pattern1*', 'template1Pattern2'],
- settings: {
- index: {
- number_of_shards: '1',
- },
- },
- mappings: {
- _source: {
- enabled: false,
+ template: {
+ settings: {
+ index: {
+ number_of_shards: '1',
+ },
},
- properties: {
- created_at: {
- type: 'date',
- format: 'EEE MMM dd HH:mm:ss Z yyyy',
+ mappings: {
+ _source: {
+ enabled: false,
+ },
+ properties: {
+ created_at: {
+ type: 'date',
+ format: 'EEE MMM dd HH:mm:ss Z yyyy',
+ },
},
},
- },
- aliases: {
- alias1: {},
+ aliases: {
+ alias1: {},
+ },
},
});
From 1415fb7e80bfe1d4199d7f305c6e1f41288bbbb9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Fri, 27 Mar 2020 13:52:44 +0100
Subject: [PATCH 10/31] Add constant to keep track of the default index
template format
---
.../index_management/common/constants/index.ts | 1 +
.../common/constants/index_templates.ts | 12 ++++++++++++
2 files changed, 13 insertions(+)
create mode 100644 x-pack/plugins/index_management/common/constants/index_templates.ts
diff --git a/x-pack/plugins/index_management/common/constants/index.ts b/x-pack/plugins/index_management/common/constants/index.ts
index d1700f0e611c0..966e2e8e64838 100644
--- a/x-pack/plugins/index_management/common/constants/index.ts
+++ b/x-pack/plugins/index_management/common/constants/index.ts
@@ -9,6 +9,7 @@ export { BASE_PATH } from './base_path';
export { API_BASE_PATH } from './api_base_path';
export { INVALID_INDEX_PATTERN_CHARS, INVALID_TEMPLATE_NAME_CHARS } from './invalid_characters';
export * from './index_statuses';
+export { DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT } from './index_templates';
export {
UIM_APP_NAME,
diff --git a/x-pack/plugins/index_management/common/constants/index_templates.ts b/x-pack/plugins/index_management/common/constants/index_templates.ts
new file mode 100644
index 0000000000000..27f9ec7a45ed1
--- /dev/null
+++ b/x-pack/plugins/index_management/common/constants/index_templates.ts
@@ -0,0 +1,12 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+/**
+ * Up until the end of the 8.x release cycle we need to support both
+ * V1 and V2 index template formats. This constant keeps track of wether
+ * we create V1 or V2 index template format in the UI.
+ */
+export const DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT = 1;
From a96abc70e9169950499cdea68368dc77ca9ab7cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Fri, 27 Mar 2020 13:54:28 +0100
Subject: [PATCH 11/31] Fix TemplateList and TS issues
---
x-pack/plugins/index_management/common/index.ts | 2 +-
x-pack/plugins/index_management/common/types/indices.ts | 2 +-
.../components/template_form/steps/step_review.tsx | 4 ++--
.../application/components/template_form/template_form.tsx | 5 ++---
.../sections/home/template_list/template_list.tsx | 2 +-
5 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/x-pack/plugins/index_management/common/index.ts b/x-pack/plugins/index_management/common/index.ts
index ac9db87c6a72b..aa24f3a616a83 100644
--- a/x-pack/plugins/index_management/common/index.ts
+++ b/x-pack/plugins/index_management/common/index.ts
@@ -4,6 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export { PLUGIN, API_BASE_PATH } from './constants';
+export { PLUGIN, API_BASE_PATH, DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT } from './constants';
export * from './types';
diff --git a/x-pack/plugins/index_management/common/types/indices.ts b/x-pack/plugins/index_management/common/types/indices.ts
index 84421c4effad5..ecf5ba21fe60c 100644
--- a/x-pack/plugins/index_management/common/types/indices.ts
+++ b/x-pack/plugins/index_management/common/types/indices.ts
@@ -5,7 +5,7 @@
*/
interface IndexModule {
- number_of_shards: number;
+ number_of_shards: number | string;
codec: string;
routing_partition_size: number;
load_fixed_bitset_filters_eagerly: boolean;
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
index 0235a1ba651bb..cb124a6741981 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
@@ -52,10 +52,10 @@ const getDescriptionText = (data: any) => {
};
export const StepReview: React.FunctionComponent = ({ template, updateCurrentStep }) => {
- const { name, indexPatterns, version, order } = template;
+ const { name, indexPatterns, version, order } = template!;
const serializedTemplate = serializeV1Template(
- stripEmptyFields(template) as TemplateDeserialized
+ stripEmptyFields(template!) as TemplateDeserialized
);
// Name not included in ES request body
delete serializedTemplate.name;
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
index 97a79e56fb4e6..0da8a12521306 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
@@ -15,7 +15,7 @@ import {
} from '@elastic/eui';
import { serializers } from '../../../shared_imports';
-import { TemplateDeserialized } from '../../../../common';
+import { TemplateDeserialized, DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT } from '../../../../common';
import { TemplateSteps } from './template_steps';
import { StepAliases, StepLogistics, StepMappings, StepSettings, StepReview } from './steps';
import { StepProps, DataGetterFunc } from './types';
@@ -53,8 +53,7 @@ export const TemplateForm: React.FunctionComponent = ({
template: {},
isManaged: false,
_kbnMeta: {
- // When the V2 API will be ready, we will create V2 template format
- formatVersion: 1,
+ formatVersion: DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT,
},
},
onSave,
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
index 53f4913c4bb8e..7bdaf776ef1c5 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
@@ -48,7 +48,7 @@ export const TemplateList: React.FunctionComponent (templates !== null ? templates.filter(template => !template.name.startsWith('.')) : []),
+ () => (templates ? templates.filter(template => !template.name.startsWith('.')) : []),
[templates]
);
From b936e31ac8f4267e9d6d3c0f31527d2276e3c6d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Fri, 27 Mar 2020 14:11:34 +0100
Subject: [PATCH 12/31] Fix component integration tests
---
.../template_clone.test.tsx | 6 +--
.../template_create.test.tsx | 38 ++++++++-------
.../client_integration/template_edit.test.tsx | 47 +++++++++++--------
.../template_form/steps/use_json_step.ts | 4 +-
.../test/fixtures/template.ts | 24 ++++++----
5 files changed, 71 insertions(+), 48 deletions(-)
diff --git a/x-pack/plugins/index_management/__jest__/client_integration/template_clone.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_clone.test.tsx
index 5d895c8e98624..f95dbe49b53da 100644
--- a/x-pack/plugins/index_management/__jest__/client_integration/template_clone.test.tsx
+++ b/x-pack/plugins/index_management/__jest__/client_integration/template_clone.test.tsx
@@ -54,10 +54,8 @@ describe('', () => {
const templateToClone = getTemplate({
name: TEMPLATE_NAME,
indexPatterns: ['indexPattern1'],
- mappings: {
- ...MAPPINGS,
- _meta: {},
- _source: {},
+ template: {
+ mappings: MAPPINGS,
},
});
diff --git a/x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx
index 981067c09f8aa..68c46af89af9b 100644
--- a/x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx
+++ b/x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx
@@ -6,6 +6,7 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
+import { DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT } from '../../common';
import { setupEnvironment, pageHelpers, nextTick } from './helpers';
import { TemplateFormTestBed } from './helpers/template_form.helpers';
import {
@@ -338,29 +339,34 @@ describe('', () => {
const latestRequest = server.requests[server.requests.length - 1];
- const expected = JSON.stringify({
+ const expected = {
isManaged: false,
name: TEMPLATE_NAME,
indexPatterns: DEFAULT_INDEX_PATTERNS,
- settings: SETTINGS,
- mappings: {
- ...MAPPINGS,
- properties: {
- [BOOLEAN_MAPPING_FIELD.name]: {
- type: BOOLEAN_MAPPING_FIELD.type,
- },
- [TEXT_MAPPING_FIELD.name]: {
- type: TEXT_MAPPING_FIELD.type,
- },
- [KEYWORD_MAPPING_FIELD.name]: {
- type: KEYWORD_MAPPING_FIELD.type,
+ template: {
+ settings: SETTINGS,
+ mappings: {
+ ...MAPPINGS,
+ properties: {
+ [BOOLEAN_MAPPING_FIELD.name]: {
+ type: BOOLEAN_MAPPING_FIELD.type,
+ },
+ [TEXT_MAPPING_FIELD.name]: {
+ type: TEXT_MAPPING_FIELD.type,
+ },
+ [KEYWORD_MAPPING_FIELD.name]: {
+ type: KEYWORD_MAPPING_FIELD.type,
+ },
},
},
+ aliases: ALIASES,
},
- aliases: ALIASES,
- });
+ _kbnMeta: {
+ formatVersion: DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT,
+ },
+ };
- expect(JSON.parse(latestRequest.requestBody).body).toEqual(expected);
+ expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual(expected);
});
it('should surface the API errors from the put HTTP request', async () => {
diff --git a/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx
index 537b0d8ef4156..b28e18f977ef1 100644
--- a/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx
+++ b/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx
@@ -99,7 +99,9 @@ describe('', () => {
const templateToEdit = fixtures.getTemplate({
name: TEMPLATE_NAME,
indexPatterns: ['indexPattern1'],
- mappings: MAPPING,
+ template: {
+ mappings: MAPPING,
+ },
});
beforeEach(async () => {
@@ -128,8 +130,7 @@ describe('', () => {
expect(nameInput.props().disabled).toEqual(true);
});
- // TODO: Flakey test
- describe.skip('form payload', () => {
+ describe('form payload', () => {
beforeEach(async () => {
const { actions, component, find, form } = testBed;
@@ -147,14 +148,18 @@ describe('', () => {
actions.clickEditButtonAtField(0);
await nextTick();
component.update();
+
// verify edit field flyout
expect(find('mappingsEditorFieldEdit').length).toEqual(1);
+
// change field name
form.setInputValue('nameParameterInput', UPDATED_MAPPING_TEXT_FIELD_NAME);
+
// Save changes
actions.clickEditFieldUpdateButton();
await nextTick();
component.update();
+
// Proceed to the next step
actions.clickNextButton();
await nextTick(50);
@@ -182,27 +187,31 @@ describe('', () => {
version,
order,
indexPatterns: UPDATED_INDEX_PATTERN,
- mappings: {
- ...MAPPING,
- _meta: {},
- _source: {},
- properties: {
- [UPDATED_MAPPING_TEXT_FIELD_NAME]: {
- type: 'text',
- store: false,
- index: true,
- fielddata: false,
- eager_global_ordinals: false,
- index_phrases: false,
- norms: true,
- index_options: 'positions',
+ template: {
+ mappings: {
+ ...MAPPING,
+ properties: {
+ [UPDATED_MAPPING_TEXT_FIELD_NAME]: {
+ type: 'text',
+ store: false,
+ index: true,
+ fielddata: false,
+ eager_global_ordinals: false,
+ index_phrases: false,
+ norms: true,
+ index_options: 'positions',
+ },
},
},
+ settings: SETTINGS,
+ aliases: ALIASES,
},
isManaged: false,
- settings: SETTINGS,
- aliases: ALIASES,
+ _kbnMeta: {
+ formatVersion: 1,
+ },
};
+
expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual(expected);
});
});
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts b/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts
index 54b18e27365f5..1efa86243ab89 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts
+++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts
@@ -47,7 +47,9 @@ export const useJsonStep = ({
const dataGetter = useCallback(() => {
const isValid = validateContent();
const value = isValid && content.trim() !== '' ? JSON.parse(content) : {};
- const data = { [prop]: value };
+ // If no key has been added to the JSON object, we strip it out so an empty objecct is not sent in the request
+ const data = { [prop]: Object.keys(value).length > 0 ? value : undefined };
+
return Promise.resolve({ isValid, data, path: 'template' });
}, [content, validateContent, prop]);
diff --git a/x-pack/plugins/index_management/test/fixtures/template.ts b/x-pack/plugins/index_management/test/fixtures/template.ts
index b3954776249fc..055c32d5cd5e4 100644
--- a/x-pack/plugins/index_management/test/fixtures/template.ts
+++ b/x-pack/plugins/index_management/test/fixtures/template.ts
@@ -5,24 +5,32 @@
*/
import { getRandomString, getRandomNumber } from '../../../../test_utils';
-import { TemplateDeserialized } from '../../common';
+import { TemplateDeserialized, DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT } from '../../common';
export const getTemplate = ({
name = getRandomString(),
version = getRandomNumber(),
order = getRandomNumber(),
indexPatterns = [],
- settings,
- aliases,
- mappings,
+ template: { settings, aliases, mappings } = {},
isManaged = false,
-}: Partial = {}): TemplateDeserialized => ({
+ templateFormatVersion = DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT,
+}: Partial<
+ TemplateDeserialized & {
+ templateFormatVersion?: 1 | 2;
+ }
+> = {}): TemplateDeserialized => ({
name,
version,
order,
indexPatterns,
- settings,
- aliases,
- mappings,
+ template: {
+ aliases,
+ mappings,
+ settings,
+ },
isManaged,
+ _kbnMeta: {
+ formatVersion: templateFormatVersion,
+ },
});
From c2dc9f031aa1ec328cc2a9b13ec99eaf140aeea4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Fri, 27 Mar 2020 14:40:07 +0100
Subject: [PATCH 13/31] Set default template name to empty string
---
.../application/components/template_form/template_form.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
index 0da8a12521306..bf0effef8544f 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
@@ -48,7 +48,7 @@ const stepComponentMap: { [key: number]: React.FunctionComponent } =
export const TemplateForm: React.FunctionComponent = ({
defaultValue = {
- name: 'untitled',
+ name: '',
indexPatterns: [],
template: {},
isManaged: false,
From 199763c595bd8e40522295afbc1b6f62a405ae75 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Fri, 27 Mar 2020 15:37:33 +0100
Subject: [PATCH 14/31] Add support for v2 format serialization in step review
---
.../plugins/index_management/common/index.ts | 2 +
.../index_management/common/lib/index.ts | 2 +
.../common/lib/template_serialization.ts | 25 +++++++++--
.../index_management/common/lib/utils.ts | 11 +++++
.../common/types/templates.ts | 45 ++++++++-----------
.../template_form/steps/step_review.tsx | 32 ++++++++-----
6 files changed, 76 insertions(+), 41 deletions(-)
diff --git a/x-pack/plugins/index_management/common/index.ts b/x-pack/plugins/index_management/common/index.ts
index aa24f3a616a83..d9079e7d3bbf2 100644
--- a/x-pack/plugins/index_management/common/index.ts
+++ b/x-pack/plugins/index_management/common/index.ts
@@ -6,4 +6,6 @@
export { PLUGIN, API_BASE_PATH, DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT } from './constants';
+export { getTemplateSetting } from './lib';
+
export * from './types';
diff --git a/x-pack/plugins/index_management/common/lib/index.ts b/x-pack/plugins/index_management/common/lib/index.ts
index 5270740edaed0..7b338b911bf06 100644
--- a/x-pack/plugins/index_management/common/lib/index.ts
+++ b/x-pack/plugins/index_management/common/lib/index.ts
@@ -8,3 +8,5 @@ export {
deserializeV1Template,
serializeV1Template,
} from './template_serialization';
+
+export { getTemplateSetting } from './utils';
diff --git a/x-pack/plugins/index_management/common/lib/template_serialization.ts b/x-pack/plugins/index_management/common/lib/template_serialization.ts
index 7b4497b817f20..069204046211d 100644
--- a/x-pack/plugins/index_management/common/lib/template_serialization.ts
+++ b/x-pack/plugins/index_management/common/lib/template_serialization.ts
@@ -3,8 +3,12 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import { TemplateDeserialized, TemplateV1Serialized, TemplateListItem } from '../types';
-import { getTemplateVersion } from './utils';
+import {
+ TemplateDeserialized,
+ TemplateV1Serialized,
+ TemplateV2Serialized,
+ TemplateListItem,
+} from '../types';
const hasEntries = (data: object = {}) => Object.entries(data).length > 0;
@@ -30,6 +34,21 @@ export function serializeV1Template(template: TemplateDeserialized): TemplateV1S
return serializedTemplate;
}
+export function serializeV2Template(template: TemplateDeserialized): TemplateV2Serialized {
+ const { aliases, mappings, settings, ...templateV1serialized } = serializeV1Template(template);
+
+ return {
+ ...templateV1serialized,
+ template: {
+ aliases,
+ mappings,
+ settings,
+ },
+ priority: template.priority,
+ composed_of: template.composedOf,
+ };
+}
+
export function deserializeV1Template(
templateEs: TemplateV1Serialized,
managedTemplatePrefix?: string
@@ -57,7 +76,7 @@ export function deserializeV1Template(
ilmPolicy: settings && settings.index && settings.index.lifecycle,
isManaged: Boolean(managedTemplatePrefix && name.startsWith(managedTemplatePrefix)),
_kbnMeta: {
- formatVersion: getTemplateVersion(templateEs),
+ formatVersion: 1,
},
};
diff --git a/x-pack/plugins/index_management/common/lib/utils.ts b/x-pack/plugins/index_management/common/lib/utils.ts
index 20444dd03c704..60eecfa5980be 100644
--- a/x-pack/plugins/index_management/common/lib/utils.ts
+++ b/x-pack/plugins/index_management/common/lib/utils.ts
@@ -16,3 +16,14 @@ export const getTemplateVersion = (
): 1 | 2 => {
return {}.hasOwnProperty.call(template, 'template') ? 2 : 1;
};
+
+export const getTemplateSetting = (
+ template: TemplateV1Serialized | TemplateV2Serialized,
+ setting: 'aliases' | 'settings' | 'mappings'
+) => {
+ const formatVersion = getTemplateVersion(template);
+
+ return formatVersion === 1
+ ? (template as TemplateV1Serialized)[setting]
+ : (template as TemplateV2Serialized).template[setting];
+};
diff --git a/x-pack/plugins/index_management/common/types/templates.ts b/x-pack/plugins/index_management/common/types/templates.ts
index 2d258f096710e..38ff1fc62504b 100644
--- a/x-pack/plugins/index_management/common/types/templates.ts
+++ b/x-pack/plugins/index_management/common/types/templates.ts
@@ -26,38 +26,14 @@ export interface TemplateV2Serialized {
name: string;
index_patterns: string[];
version?: number;
+ priority?: number;
order?: number;
template: {
settings?: IndexSettings;
aliases?: Aliases;
mappings?: Mappings;
};
-}
-
-// Template Deserialized
-interface TemplateBaseDeserialized {
- name: string;
- indexPatterns: string[];
- version?: number;
- order?: number;
- ilmPolicy?: {
- name: string;
- };
- isManaged: boolean;
-}
-
-export interface TemplateV1Deserialized extends TemplateBaseDeserialized {
- settings?: IndexSettings;
- aliases?: Aliases;
- mappings?: Mappings;
-}
-
-export interface TemplateV2Deserialized extends TemplateBaseDeserialized {
- template: {
- settings?: IndexSettings;
- aliases?: Aliases;
- mappings?: Mappings;
- };
+ composed_of?: string[];
}
/**
@@ -84,8 +60,23 @@ export interface TemplateListItem {
* The UI will only be dealing with this interface, conversion from and to V1 format
* is done server side.
*/
-export interface TemplateDeserialized extends TemplateV2Deserialized {
+export interface TemplateDeserialized {
+ name: string;
+ indexPatterns: string[];
+ isManaged: boolean;
+ template: {
+ settings?: IndexSettings;
+ aliases?: Aliases;
+ mappings?: Mappings;
+ };
_kbnMeta: {
formatVersion: 1 | 2;
};
+ version?: number;
+ priority?: number;
+ order?: number;
+ ilmPolicy?: {
+ name: string;
+ };
+ composedOf?: string[];
}
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
index cb124a6741981..05a173b571937 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
@@ -22,8 +22,11 @@ import {
import { FormattedMessage } from '@kbn/i18n/react';
import { serializers } from '../../../../shared_imports';
-import { serializeV1Template } from '../../../../../common/lib/template_serialization';
-import { TemplateDeserialized } from '../../../../../common';
+import {
+ serializeV1Template,
+ serializeV2Template,
+} from '../../../../../common/lib/template_serialization';
+import { TemplateDeserialized, getTemplateSetting } from '../../../../../common';
import { StepProps } from '../types';
const { stripEmptyFields } = serializers;
@@ -52,18 +55,25 @@ const getDescriptionText = (data: any) => {
};
export const StepReview: React.FunctionComponent = ({ template, updateCurrentStep }) => {
- const { name, indexPatterns, version, order } = template!;
+ const {
+ name,
+ indexPatterns,
+ version,
+ order,
+ _kbnMeta: { formatVersion },
+ } = template!;
+
+ const serializedTemplate =
+ formatVersion === 1
+ ? serializeV1Template(stripEmptyFields(template!) as TemplateDeserialized)
+ : serializeV2Template(stripEmptyFields(template!) as TemplateDeserialized);
- const serializedTemplate = serializeV1Template(
- stripEmptyFields(template!) as TemplateDeserialized
- );
// Name not included in ES request body
delete serializedTemplate.name;
- const {
- mappings: serializedMappings,
- settings: serializedSettings,
- aliases: serializedAliases,
- } = serializedTemplate;
+
+ const serializedMappings = getTemplateSetting(serializedTemplate, 'mappings');
+ const serializedSettings = getTemplateSetting(serializedTemplate, 'settings');
+ const serializedAliases = getTemplateSetting(serializedTemplate, 'aliases');
const numIndexPatterns = indexPatterns!.length;
From 8b0caf2451d95699c6181e433ed0c704df5530c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Fri, 27 Mar 2020 15:47:03 +0100
Subject: [PATCH 15/31] Add deserializer for V2 template format (server side)
---
.../common/lib/template_serialization.ts | 38 ++++++++++++++-----
1 file changed, 28 insertions(+), 10 deletions(-)
diff --git a/x-pack/plugins/index_management/common/lib/template_serialization.ts b/x-pack/plugins/index_management/common/lib/template_serialization.ts
index 069204046211d..6f4f0f9ef45e9 100644
--- a/x-pack/plugins/index_management/common/lib/template_serialization.ts
+++ b/x-pack/plugins/index_management/common/lib/template_serialization.ts
@@ -49,8 +49,8 @@ export function serializeV2Template(template: TemplateDeserialized): TemplateV2S
};
}
-export function deserializeV1Template(
- templateEs: TemplateV1Serialized,
+export function deserializeV2Template(
+ templateEs: TemplateV2Serialized,
managedTemplatePrefix?: string
): TemplateDeserialized {
const {
@@ -58,23 +58,22 @@ export function deserializeV1Template(
version,
order,
index_patterns: indexPatterns,
- settings,
- aliases,
- mappings,
+ template,
+ priority,
+ composed_of: composedOf,
} = templateEs;
+ const { settings } = template;
const deserializedTemplate: TemplateDeserialized = {
name,
version,
order,
indexPatterns: indexPatterns.sort(),
- template: {
- settings,
- aliases,
- mappings,
- },
+ template,
ilmPolicy: settings && settings.index && settings.index.lifecycle,
isManaged: Boolean(managedTemplatePrefix && name.startsWith(managedTemplatePrefix)),
+ priority,
+ composedOf,
_kbnMeta: {
formatVersion: 1,
},
@@ -83,6 +82,25 @@ export function deserializeV1Template(
return deserializedTemplate;
}
+export function deserializeV1Template(
+ templateEs: TemplateV1Serialized,
+ managedTemplatePrefix?: string
+): TemplateDeserialized {
+ const { settings, aliases, mappings, ...rest } = templateEs;
+
+ const deserializedTemplateV2 = deserializeV2Template(
+ { ...rest, template: { aliases, settings, mappings } },
+ managedTemplatePrefix
+ );
+
+ return {
+ ...deserializedTemplateV2,
+ _kbnMeta: {
+ formatVersion: 1,
+ },
+ };
+}
+
export function deserializeTemplateList(
indexTemplatesByName: { [key: string]: Omit },
managedTemplatePrefix?: string
From 7fb48f892eeae053520786921cf286c19d093af3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Fri, 27 Mar 2020 16:48:33 +0100
Subject: [PATCH 16/31] Only allow template V1 to be created or edited
---
.../routes/api/templates/register_create_route.ts | 10 ++++++++++
.../routes/api/templates/register_update_route.ts | 8 ++++++++
2 files changed, 18 insertions(+)
diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts
index e22246c7f2b49..22715e457a832 100644
--- a/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts
+++ b/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts
@@ -20,6 +20,16 @@ export function registerCreateRoute({ router, license, lib }: RouteDependencies)
license.guardApiRoute(async (ctx, req, res) => {
const { callAsCurrentUser } = ctx.core.elasticsearch.dataClient;
const template = req.body as TemplateDeserialized;
+ const {
+ _kbnMeta: { formatVersion },
+ } = template;
+
+ if (formatVersion !== 1) {
+ return res.badRequest({ body: 'Only index template version 1 can be created.' });
+ }
+
+ // For now we format to V1 index templates.
+ // When the V2 API is ready we will only create V2 template format.
const serializedTemplate = serializeV1Template(template);
const {
diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts
index e0b5085fa3914..2df72bec9d252 100644
--- a/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts
+++ b/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts
@@ -26,6 +26,14 @@ export function registerUpdateRoute({ router, license, lib }: RouteDependencies)
const { callAsCurrentUser } = ctx.core.elasticsearch.dataClient;
const { name } = req.params as typeof paramsSchema.type;
const template = req.body as TemplateDeserialized;
+ const {
+ _kbnMeta: { formatVersion },
+ } = template;
+
+ if (formatVersion !== 1) {
+ return res.badRequest({ body: 'Only index template version 1 can be edited.' });
+ }
+
const serializedTemplate = serializeV1Template(template);
const { order, index_patterns, version, settings, mappings, aliases } = serializedTemplate;
From 2a3fc015af13e7535330fbd13cf749c879a9d63e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Mon, 30 Mar 2020 14:30:35 +0200
Subject: [PATCH 17/31] Require version to be provided when fetching template
---
.../common/types/templates.ts | 7 ++++-
.../public/application/lib/index_templates.ts | 17 ++++++++++++
.../template_details/template_details.tsx | 21 ++++++++-------
.../home/template_list/template_list.tsx | 27 ++++++++++++-------
.../template_table/template_table.tsx | 18 ++++++-------
.../template_clone/template_clone.tsx | 14 +++++++---
.../template_create/template_create.tsx | 3 ++-
.../sections/template_edit/template_edit.tsx | 15 ++++++++---
.../public/application/services/api.ts | 14 ++++++++--
.../public/application/services/routing.ts | 23 +++++++++++-----
.../api/templates/register_get_routes.ts | 18 +++++++++++--
11 files changed, 130 insertions(+), 47 deletions(-)
create mode 100644 x-pack/plugins/index_management/public/application/lib/index_templates.ts
diff --git a/x-pack/plugins/index_management/common/types/templates.ts b/x-pack/plugins/index_management/common/types/templates.ts
index 38ff1fc62504b..51047ee1042ae 100644
--- a/x-pack/plugins/index_management/common/types/templates.ts
+++ b/x-pack/plugins/index_management/common/types/templates.ts
@@ -53,6 +53,9 @@ export interface TemplateListItem {
name: string;
};
isManaged: boolean;
+ _kbnMeta: {
+ formatVersion: IndexTemplateFormatVersion;
+ };
}
/**
@@ -70,7 +73,7 @@ export interface TemplateDeserialized {
mappings?: Mappings;
};
_kbnMeta: {
- formatVersion: 1 | 2;
+ formatVersion: IndexTemplateFormatVersion;
};
version?: number;
priority?: number;
@@ -80,3 +83,5 @@ export interface TemplateDeserialized {
};
composedOf?: string[];
}
+
+export type IndexTemplateFormatVersion = 1 | 2;
diff --git a/x-pack/plugins/index_management/public/application/lib/index_templates.ts b/x-pack/plugins/index_management/public/application/lib/index_templates.ts
new file mode 100644
index 0000000000000..ab02ceea1030c
--- /dev/null
+++ b/x-pack/plugins/index_management/public/application/lib/index_templates.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { parse } from 'query-string';
+import { Location } from 'history';
+
+export const getFormatVersionFromQueryparams = (location: Location): 1 | 2 | undefined => {
+ const { v } = parse(location.search.substring(1));
+
+ if (!Boolean(v) || typeof v !== 'string') {
+ return undefined;
+ }
+
+ return +v as 1 | 2;
+};
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx
index 9b9cd649e5f98..2fd73fe647d86 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx
@@ -30,7 +30,7 @@ import {
UIM_TEMPLATE_DETAIL_PANEL_SETTINGS_TAB,
UIM_TEMPLATE_DETAIL_PANEL_ALIASES_TAB,
} from '../../../../../../common/constants';
-import { TemplateDeserialized } from '../../../../../../common';
+import { TemplateDeserialized, IndexTemplateFormatVersion } from '../../../../../../common';
import { TemplateDeleteModal, SectionLoading, SectionError, Error } from '../../../../components';
import { useLoadIndexTemplate } from '../../../../services/api';
import { decodePath } from '../../../../services/routing';
@@ -39,10 +39,10 @@ import { useServices } from '../../../../app_context';
import { TabSummary, TabMappings, TabSettings, TabAliases } from './tabs';
interface Props {
- templateName: TemplateDeserialized['name'];
+ template: { name: string; formatVersion: IndexTemplateFormatVersion };
onClose: () => void;
- editTemplate: (templateName: TemplateDeserialized['name']) => void;
- cloneTemplate: (templateName: TemplateDeserialized['name']) => void;
+ editTemplate: (name: string, formatVersion: IndexTemplateFormatVersion) => void;
+ cloneTemplate: (name: string, formatVersion: IndexTemplateFormatVersion) => void;
reload: () => Promise;
}
@@ -95,7 +95,7 @@ const tabToUiMetricMap: { [key: string]: string } = {
};
export const TemplateDetails: React.FunctionComponent = ({
- templateName,
+ template: { name: templateName, formatVersion },
onClose,
editTemplate,
cloneTemplate,
@@ -103,9 +103,12 @@ export const TemplateDetails: React.FunctionComponent = ({
}) => {
const { uiMetricService } = useServices();
const decodedTemplateName = decodePath(templateName);
- const { error, data: templateDetails, isLoading } = useLoadIndexTemplate(decodedTemplateName);
+ const { error, data: templateDetails, isLoading } = useLoadIndexTemplate(
+ decodedTemplateName,
+ formatVersion
+ );
const isManaged = templateDetails?.isManaged;
- const [templateToDelete, setTemplateToDelete] = useState>([]);
+ const [templateToDelete, setTemplateToDelete] = useState([]);
const [activeTab, setActiveTab] = useState(SUMMARY_TAB_ID);
const [isPopoverOpen, setIsPopOverOpen] = useState(false);
@@ -274,7 +277,7 @@ export const TemplateDetails: React.FunctionComponent = ({
defaultMessage: 'Edit',
}),
icon: 'pencil',
- onClick: () => editTemplate(decodedTemplateName),
+ onClick: () => editTemplate(templateName, formatVersion),
disabled: isManaged,
},
{
@@ -282,7 +285,7 @@ export const TemplateDetails: React.FunctionComponent = ({
defaultMessage: 'Clone',
}),
icon: 'copy',
- onClick: () => cloneTemplate(decodedTemplateName),
+ onClick: () => cloneTemplate(templateName, formatVersion),
},
{
name: i18n.translate(
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
index 7bdaf776ef1c5..1e84202639ee8 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
@@ -16,31 +16,35 @@ import {
EuiFlexItem,
EuiFlexGroup,
} from '@elastic/eui';
+
+import { UIM_TEMPLATE_LIST_LOAD } from '../../../../../common/constants';
+import { IndexTemplateFormatVersion } from '../../../../../common';
import { SectionError, SectionLoading, Error } from '../../../components';
-import { TemplateTable } from './template_table';
import { useLoadIndexTemplates } from '../../../services/api';
-import { TemplateDeserialized } from '../../../../../common';
import { useServices } from '../../../app_context';
import {
getTemplateEditLink,
getTemplateListLink,
getTemplateCloneLink,
} from '../../../services/routing';
-import { UIM_TEMPLATE_LIST_LOAD } from '../../../../../common/constants';
+import { getFormatVersionFromQueryparams } from '../../../lib/index_templates';
+import { TemplateTable } from './template_table';
import { TemplateDetails } from './template_details';
interface MatchParams {
- templateName?: TemplateDeserialized['name'];
+ templateName?: string;
}
export const TemplateList: React.FunctionComponent> = ({
match: {
params: { templateName },
},
+ location,
history,
}) => {
const { uiMetricService } = useServices();
const { error, isLoading, data: templates, sendRequest: reload } = useLoadIndexTemplates();
+ const queryParamsFormatVersion = getFormatVersionFromQueryparams(location);
let content;
@@ -56,12 +60,12 @@ export const TemplateList: React.FunctionComponent {
- history.push(getTemplateEditLink(name));
+ const editTemplate = (name: string, formatVersion: IndexTemplateFormatVersion) => {
+ history.push(getTemplateEditLink(name, formatVersion));
};
- const cloneTemplate = (name: TemplateDeserialized['name']) => {
- history.push(getTemplateCloneLink(name));
+ const cloneTemplate = (name: string, formatVersion: IndexTemplateFormatVersion) => {
+ history.push(getTemplateCloneLink(name, formatVersion));
};
// Track component loaded
@@ -148,9 +152,12 @@ export const TemplateList: React.FunctionComponent
{content}
- {templateName && (
+ {templateName && queryParamsFormatVersion !== undefined && (
Promise;
- editTemplate: (name: TemplateDeserialized['name']) => void;
- cloneTemplate: (name: TemplateDeserialized['name']) => void;
+ editTemplate: (name: string, formatVersion: IndexTemplateFormatVersion) => void;
+ cloneTemplate: (name: string, formatVersion: IndexTemplateFormatVersion) => void;
}
export const TemplateTable: React.FunctionComponent = ({
@@ -40,11 +40,11 @@ export const TemplateTable: React.FunctionComponent = ({
}),
truncateText: true,
sortable: true,
- render: (name: TemplateListItem['name']) => {
+ render: (name: TemplateListItem['name'], item: TemplateListItem) => {
return (
/* eslint-disable-next-line @elastic/eui/href-or-on-click */
uiMetricService.trackMetric('click', UIM_TEMPLATE_SHOW_DETAILS_CLICK)}
>
@@ -133,8 +133,8 @@ export const TemplateTable: React.FunctionComponent = ({
}),
icon: 'pencil',
type: 'icon',
- onClick: ({ name }: TemplateListItem) => {
- editTemplate(name);
+ onClick: ({ name, _kbnMeta: { formatVersion } }: TemplateListItem) => {
+ editTemplate(name, formatVersion);
},
enabled: ({ isManaged }: TemplateListItem) => !isManaged,
},
@@ -147,8 +147,8 @@ export const TemplateTable: React.FunctionComponent = ({
defaultMessage: 'Clone this template',
}),
icon: 'copy',
- onClick: ({ name }: TemplateListItem) => {
- cloneTemplate(name);
+ onClick: ({ name, _kbnMeta: { formatVersion } }: TemplateListItem) => {
+ cloneTemplate(name, formatVersion);
},
},
{
diff --git a/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx b/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx
index ff0739177703e..b69e441feb176 100644
--- a/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx
@@ -7,11 +7,13 @@ import React, { useEffect, useState } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiPageBody, EuiPageContent, EuiSpacer, EuiTitle } from '@elastic/eui';
+
+import { TemplateDeserialized, DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT } from '../../../../common';
import { TemplateForm, SectionLoading, SectionError, Error } from '../../components';
import { breadcrumbService } from '../../services/breadcrumbs';
import { decodePath, getTemplateDetailsLink } from '../../services/routing';
-import { TemplateDeserialized } from '../../../../common';
import { saveTemplate, useLoadIndexTemplate } from '../../services/api';
+import { getFormatVersionFromQueryparams } from '../../lib/index_templates';
interface MatchParams {
name: string;
@@ -21,14 +23,18 @@ export const TemplateClone: React.FunctionComponent {
const decodedTemplateName = decodePath(name);
+ const formatVersion =
+ getFormatVersionFromQueryparams(location) ?? DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT;
+
const [isSaving, setIsSaving] = useState(false);
const [saveError, setSaveError] = useState(null);
-
const { error: templateToCloneError, data: templateToClone, isLoading } = useLoadIndexTemplate(
- decodedTemplateName
+ decodedTemplateName,
+ formatVersion
);
const onSave = async (template: TemplateDeserialized) => {
@@ -46,7 +52,7 @@ export const TemplateClone: React.FunctionComponent {
diff --git a/x-pack/plugins/index_management/public/application/sections/template_create/template_create.tsx b/x-pack/plugins/index_management/public/application/sections/template_create/template_create.tsx
index cfd4272334501..27341685f3dc0 100644
--- a/x-pack/plugins/index_management/public/application/sections/template_create/template_create.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/template_create/template_create.tsx
@@ -7,6 +7,7 @@ import React, { useEffect, useState } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiPageBody, EuiPageContent, EuiSpacer, EuiTitle } from '@elastic/eui';
+
import { TemplateForm } from '../../components';
import { breadcrumbService } from '../../services/breadcrumbs';
import { TemplateDeserialized } from '../../../../common';
@@ -32,7 +33,7 @@ export const TemplateCreate: React.FunctionComponent = ({ h
return;
}
- history.push(getTemplateDetailsLink(name));
+ history.push(getTemplateDetailsLink(name, template._kbnMeta.formatVersion));
};
const clearSaveError = () => {
diff --git a/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx b/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx
index f4315f3bea8e9..9ad26d0af802d 100644
--- a/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx
@@ -7,11 +7,13 @@ import React, { useEffect, useState, Fragment } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiPageBody, EuiPageContent, EuiTitle, EuiSpacer, EuiCallOut } from '@elastic/eui';
+
+import { TemplateDeserialized, DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT } from '../../../../common';
import { breadcrumbService } from '../../services/breadcrumbs';
import { useLoadIndexTemplate, updateTemplate } from '../../services/api';
import { decodePath, getTemplateDetailsLink } from '../../services/routing';
import { SectionLoading, SectionError, TemplateForm, Error } from '../../components';
-import { TemplateDeserialized } from '../../../../common';
+import { getFormatVersionFromQueryparams } from '../../lib/index_templates';
interface MatchParams {
name: string;
@@ -21,13 +23,20 @@ export const TemplateEdit: React.FunctionComponent {
const decodedTemplateName = decodePath(name);
+ const formatVersion =
+ getFormatVersionFromQueryparams(location) ?? DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT;
+
const [isSaving, setIsSaving] = useState(false);
const [saveError, setSaveError] = useState(null);
- const { error, data: template, isLoading } = useLoadIndexTemplate(decodedTemplateName);
+ const { error, data: template, isLoading } = useLoadIndexTemplate(
+ decodedTemplateName,
+ formatVersion
+ );
useEffect(() => {
breadcrumbService.setBreadcrumbs('templateEdit');
@@ -46,7 +55,7 @@ export const TemplateEdit: React.FunctionComponent {
diff --git a/x-pack/plugins/index_management/public/application/services/api.ts b/x-pack/plugins/index_management/public/application/services/api.ts
index 82944cc3db5bf..d5ccc49c561a7 100644
--- a/x-pack/plugins/index_management/public/application/services/api.ts
+++ b/x-pack/plugins/index_management/public/application/services/api.ts
@@ -37,7 +37,11 @@ import { TAB_SETTINGS, TAB_MAPPING, TAB_STATS } from '../constants';
import { useRequest, sendRequest } from './use_request';
import { httpService } from './http';
import { UiMetricService } from './ui_metric';
-import { TemplateDeserialized, TemplateListItem } from '../../../common';
+import {
+ TemplateDeserialized,
+ TemplateListItem,
+ IndexTemplateFormatVersion,
+} from '../../../common';
import { IndexMgmtMetricsType } from '../../types';
// Temporary hack to provide the uiMetricService instance to this file.
@@ -220,10 +224,16 @@ export async function deleteTemplates(names: Array
return result;
}
-export function useLoadIndexTemplate(name: TemplateDeserialized['name']) {
+export function useLoadIndexTemplate(
+ name: TemplateDeserialized['name'],
+ formatVersion: IndexTemplateFormatVersion
+) {
return useRequest({
path: `${API_BASE_PATH}/templates/${encodeURIComponent(name)}`,
method: 'get',
+ query: {
+ v: formatVersion,
+ },
});
}
diff --git a/x-pack/plugins/index_management/public/application/services/routing.ts b/x-pack/plugins/index_management/public/application/services/routing.ts
index aceadab18b1ff..a6d8f67751cd1 100644
--- a/x-pack/plugins/index_management/public/application/services/routing.ts
+++ b/x-pack/plugins/index_management/public/application/services/routing.ts
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { BASE_PATH } from '../../../common/constants';
+import { IndexTemplateFormatVersion } from '../../../common';
export const getTemplateListLink = () => {
return `${BASE_PATH}templates`;
@@ -11,18 +12,28 @@ export const getTemplateListLink = () => {
// Need to add some additonal encoding/decoding logic to work with React Router
// For background, see: https://github.com/ReactTraining/history/issues/505
-export const getTemplateDetailsLink = (name: string, withHash = false) => {
- const baseUrl = `${BASE_PATH}templates/${encodeURIComponent(encodeURIComponent(name))}`;
+export const getTemplateDetailsLink = (
+ name: string,
+ formatVersion: IndexTemplateFormatVersion,
+ withHash = false
+) => {
+ const baseUrl = `${BASE_PATH}templates/${encodeURIComponent(
+ encodeURIComponent(name)
+ )}?v=${formatVersion}`;
const url = withHash ? `#${baseUrl}` : baseUrl;
return encodeURI(url);
};
-export const getTemplateEditLink = (name: string) => {
- return encodeURI(`${BASE_PATH}edit_template/${encodeURIComponent(encodeURIComponent(name))}`);
+export const getTemplateEditLink = (name: string, formatVersion: IndexTemplateFormatVersion) => {
+ return encodeURI(
+ `${BASE_PATH}edit_template/${encodeURIComponent(encodeURIComponent(name))}?v=${formatVersion}`
+ );
};
-export const getTemplateCloneLink = (name: string) => {
- return encodeURI(`${BASE_PATH}clone_template/${encodeURIComponent(encodeURIComponent(name))}`);
+export const getTemplateCloneLink = (name: string, formatVersion: IndexTemplateFormatVersion) => {
+ return encodeURI(
+ `${BASE_PATH}clone_template/${encodeURIComponent(encodeURIComponent(name))}?v=${formatVersion}`
+ );
};
export const decodePath = (pathname: string): string => {
diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts
index d4a56f87fb508..c0915c72a4b91 100644
--- a/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts
+++ b/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts
@@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import { schema } from '@kbn/config-schema';
+import { schema, TypeOf } from '@kbn/config-schema';
import { deserializeV1Template, deserializeTemplateList } from '../../../../common/lib';
import { getManagedTemplatePrefix } from '../../../lib/get_managed_templates';
@@ -29,13 +29,27 @@ const paramsSchema = schema.object({
name: schema.string(),
});
+// Require the template format version (V1 or V2) to be provided as Query param
+const querySchema = schema.object({
+ v: schema.oneOf([schema.literal('1'), schema.literal('2')]),
+});
+
export function registerGetOneRoute({ router, license, lib }: RouteDependencies) {
router.get(
- { path: addBasePath('/templates/{name}'), validate: { params: paramsSchema } },
+ {
+ path: addBasePath('/templates/{name}'),
+ validate: { params: paramsSchema, query: querySchema },
+ },
license.guardApiRoute(async (ctx, req, res) => {
const { name } = req.params as typeof paramsSchema.type;
const { callAsCurrentUser } = ctx.core.elasticsearch.dataClient;
+ const { v: version } = req.query as TypeOf;
+
+ if (version !== '1') {
+ return res.badRequest({ body: 'Only index template version 1 can be fetched.' });
+ }
+
try {
const managedTemplatePrefix = await getManagedTemplatePrefix(callAsCurrentUser);
const indexTemplateByName = await callAsCurrentUser('indices.getTemplate', { name });
From e3e3ccb99475e568441bc14856c1e9bedec7475d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Mon, 30 Mar 2020 14:37:32 +0200
Subject: [PATCH 18/31] Fix template V1 serialization
---
.../index_management/common/lib/template_serialization.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/x-pack/plugins/index_management/common/lib/template_serialization.ts b/x-pack/plugins/index_management/common/lib/template_serialization.ts
index 6f4f0f9ef45e9..6331c997d4cc3 100644
--- a/x-pack/plugins/index_management/common/lib/template_serialization.ts
+++ b/x-pack/plugins/index_management/common/lib/template_serialization.ts
@@ -18,7 +18,7 @@ export function serializeV1Template(template: TemplateDeserialized): TemplateV1S
version,
order,
indexPatterns,
- template: { settings, aliases, mappings },
+ template: { settings, aliases, mappings } = {},
} = template;
const serializedTemplate: TemplateV1Serialized = {
From d22022413529598400a3bde035c8db1d41e8f495 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Mon, 30 Mar 2020 15:41:17 +0200
Subject: [PATCH 19/31] Update API delete template route to receive template
format version
---
.../components/template_delete_modal.tsx | 21 +++++++-------
.../template_details/template_details.tsx | 7 +++--
.../template_table/template_table.tsx | 15 +++++++---
.../public/application/services/api.ts | 11 +++++---
.../api/templates/register_delete_route.ts | 28 +++++++++++++------
5 files changed, 53 insertions(+), 29 deletions(-)
diff --git a/x-pack/plugins/index_management/public/application/components/template_delete_modal.tsx b/x-pack/plugins/index_management/public/application/components/template_delete_modal.tsx
index d82024c9aabba..b80e51d8d139f 100644
--- a/x-pack/plugins/index_management/public/application/components/template_delete_modal.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_delete_modal.tsx
@@ -4,28 +4,27 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import React, { Fragment, useState } from 'react';
import { EuiConfirmModal, EuiOverlayMask, EuiCallOut, EuiCheckbox, EuiBadge } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-import React, { Fragment, useState } from 'react';
+
+import { IndexTemplateFormatVersion } from '../../../common';
import { deleteTemplates } from '../services/api';
import { notificationService } from '../services/notification';
-import { TemplateDeserialized } from '../../../common';
export const TemplateDeleteModal = ({
templatesToDelete,
callback,
}: {
- templatesToDelete: Array;
+ templatesToDelete: Array<{ name: string; formatVersion: IndexTemplateFormatVersion }>;
callback: (data?: { hasDeletedTemplates: boolean }) => void;
}) => {
const [isDeleteConfirmed, setIsDeleteConfirmed] = useState(false);
const numTemplatesToDelete = templatesToDelete.length;
- const hasSystemTemplate = Boolean(
- templatesToDelete.find(templateName => templateName.startsWith('.'))
- );
+ const hasSystemTemplate = Boolean(templatesToDelete.find(({ name }) => name.startsWith('.')));
const handleDeleteTemplates = () => {
deleteTemplates(templatesToDelete).then(({ data: { templatesDeleted, errors }, error }) => {
@@ -38,7 +37,7 @@ export const TemplateDeleteModal = ({
'xpack.idxMgmt.deleteTemplatesModal.successDeleteSingleNotificationMessageText',
{
defaultMessage: "Deleted template '{templateName}'",
- values: { templateName: templatesToDelete[0] },
+ values: { templateName: templatesToDelete[0].name },
}
)
: i18n.translate(
@@ -120,10 +119,10 @@ export const TemplateDeleteModal = ({
- {templatesToDelete.map(template => (
- -
- {template}
- {template.startsWith('.') ? (
+ {templatesToDelete.map(({ name }) => (
+
-
+ {name}
+ {name.startsWith('.') ? (
{' '}
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx
index 2fd73fe647d86..b23163667ffac 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx
@@ -108,7 +108,9 @@ export const TemplateDetails: React.FunctionComponent = ({
formatVersion
);
const isManaged = templateDetails?.isManaged;
- const [templateToDelete, setTemplateToDelete] = useState([]);
+ const [templateToDelete, setTemplateToDelete] = useState<
+ Array<{ name: string; formatVersion: IndexTemplateFormatVersion }>
+ >([]);
const [activeTab, setActiveTab] = useState(SUMMARY_TAB_ID);
const [isPopoverOpen, setIsPopOverOpen] = useState(false);
@@ -295,7 +297,8 @@ export const TemplateDetails: React.FunctionComponent = ({
}
),
icon: 'trash',
- onClick: () => setTemplateToDelete([decodedTemplateName]),
+ onClick: () =>
+ setTemplateToDelete([{ name: decodedTemplateName, formatVersion }]),
disabled: isManaged,
},
],
diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx
index 25c104a6f400f..ef583a5a1fdf2 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx
@@ -30,7 +30,9 @@ export const TemplateTable: React.FunctionComponent = ({
}) => {
const { uiMetricService } = useServices();
const [selection, setSelection] = useState([]);
- const [templatesToDelete, setTemplatesToDelete] = useState>([]);
+ const [templatesToDelete, setTemplatesToDelete] = useState<
+ Array<{ name: string; formatVersion: IndexTemplateFormatVersion }>
+ >([]);
const columns: Array> = [
{
@@ -161,8 +163,8 @@ export const TemplateTable: React.FunctionComponent = ({
icon: 'trash',
color: 'danger',
type: 'icon',
- onClick: ({ name }: TemplateListItem) => {
- setTemplatesToDelete([name]);
+ onClick: ({ name, _kbnMeta: { formatVersion } }: TemplateListItem) => {
+ setTemplatesToDelete([{ name, formatVersion }]);
},
isPrimary: true,
enabled: ({ isManaged }: TemplateListItem) => !isManaged,
@@ -205,7 +207,12 @@ export const TemplateTable: React.FunctionComponent = ({
- setTemplatesToDelete(selection.map((selected: TemplateListItem) => selected.name))
+ setTemplatesToDelete(
+ selection.map(({ name, _kbnMeta: { formatVersion } }: TemplateListItem) => ({
+ name,
+ formatVersion,
+ }))
+ )
}
color="danger"
>
diff --git a/x-pack/plugins/index_management/public/application/services/api.ts b/x-pack/plugins/index_management/public/application/services/api.ts
index d5ccc49c561a7..c09c4f44c4e3a 100644
--- a/x-pack/plugins/index_management/public/application/services/api.ts
+++ b/x-pack/plugins/index_management/public/application/services/api.ts
@@ -211,13 +211,16 @@ export function useLoadIndexTemplates() {
});
}
-export async function deleteTemplates(names: Array) {
+export async function deleteTemplates(
+ templates: Array<{ name: string; formatVersion: IndexTemplateFormatVersion }>
+) {
const result = sendRequest({
- path: `${API_BASE_PATH}/templates/${names.map(name => encodeURIComponent(name)).join(',')}`,
- method: 'delete',
+ path: `${API_BASE_PATH}/delete-templates`,
+ method: 'post',
+ body: { templates },
});
- const uimActionType = names.length > 1 ? UIM_TEMPLATE_DELETE_MANY : UIM_TEMPLATE_DELETE;
+ const uimActionType = templates.length > 1 ? UIM_TEMPLATE_DELETE_MANY : UIM_TEMPLATE_DELETE;
uiMetricService.trackMetric('count', uimActionType);
diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_delete_route.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_delete_route.ts
index 39b3b4ea9d8ef..4c8fdd0c2f1c7 100644
--- a/x-pack/plugins/index_management/server/routes/api/templates/register_delete_route.ts
+++ b/x-pack/plugins/index_management/server/routes/api/templates/register_delete_route.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { schema } from '@kbn/config-schema';
+import { schema, TypeOf } from '@kbn/config-schema';
import { RouteDependencies } from '../../../types';
import { addBasePath } from '../index';
@@ -12,27 +12,39 @@ import { wrapEsError } from '../../helpers';
import { TemplateDeserialized } from '../../../../common';
-const paramsSchema = schema.object({
- names: schema.string(),
+const bodySchema = schema.object({
+ templates: schema.arrayOf(
+ schema.object({
+ name: schema.string(),
+ formatVersion: schema.oneOf([schema.literal(1), schema.literal(2)]),
+ })
+ ),
});
export function registerDeleteRoute({ router, license }: RouteDependencies) {
- router.delete(
- { path: addBasePath('/templates/{names}'), validate: { params: paramsSchema } },
+ router.post(
+ {
+ path: addBasePath('/delete-templates'),
+ validate: { body: bodySchema },
+ },
license.guardApiRoute(async (ctx, req, res) => {
- const { names } = req.params as typeof paramsSchema.type;
- const templateNames = names.split(',');
+ const { templates } = req.body as TypeOf;
const response: { templatesDeleted: Array; errors: any[] } = {
templatesDeleted: [],
errors: [],
};
await Promise.all(
- templateNames.map(async name => {
+ templates.map(async ({ name, formatVersion }) => {
try {
+ if (formatVersion !== 1) {
+ return res.badRequest({ body: 'Only index template version 1 can be deleted.' });
+ }
+
await ctx.core.elasticsearch.dataClient.callAsCurrentUser('indices.deleteTemplate', {
name,
});
+
return response.templatesDeleted.push(name);
} catch (e) {
return response.errors.push({
From ca0f0ec8037b483cb26218e82686525dcb887b6e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Mon, 30 Mar 2020 16:05:50 +0200
Subject: [PATCH 20/31] Fix API integration tests
---
.../index_management/templates.helpers.js | 14 ++++++--------
.../apis/management/index_management/templates.js | 4 +++-
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/x-pack/test/api_integration/apis/management/index_management/templates.helpers.js b/x-pack/test/api_integration/apis/management/index_management/templates.helpers.js
index 339fedc193c7a..5d8364a8b92c2 100644
--- a/x-pack/test/api_integration/apis/management/index_management/templates.helpers.js
+++ b/x-pack/test/api_integration/apis/management/index_management/templates.helpers.js
@@ -9,7 +9,8 @@ import { API_BASE_PATH, INDEX_PATTERNS } from './constants';
export const registerHelpers = ({ supertest }) => {
const getAllTemplates = () => supertest.get(`${API_BASE_PATH}/templates`);
- const getOneTemplate = name => supertest.get(`${API_BASE_PATH}/templates/${name}`);
+ const getOneTemplate = (name, formatVersion = 1) =>
+ supertest.get(`${API_BASE_PATH}/templates/${name}?v=${formatVersion}`);
const getTemplatePayload = (name, formatVersion = 1) => ({
name,
@@ -54,14 +55,11 @@ export const registerHelpers = ({ supertest }) => {
.set('kbn-xsrf', 'xxx')
.send(payload);
- const deleteTemplates = templatesToDelete =>
+ const deleteTemplates = templates =>
supertest
- .delete(
- `${API_BASE_PATH}/templates/${templatesToDelete
- .map(template => encodeURIComponent(template))
- .join(',')}`
- )
- .set('kbn-xsrf', 'xxx');
+ .post(`${API_BASE_PATH}/delete-templates`)
+ .set('kbn-xsrf', 'xxx')
+ .send({ templates });
const updateTemplate = (payload, templateName) =>
supertest
diff --git a/x-pack/test/api_integration/apis/management/index_management/templates.js b/x-pack/test/api_integration/apis/management/index_management/templates.js
index 939a395b3ced0..63bfd494301b3 100644
--- a/x-pack/test/api_integration/apis/management/index_management/templates.js
+++ b/x-pack/test/api_integration/apis/management/index_management/templates.js
@@ -154,7 +154,9 @@ export default function({ getService }) {
templateName
);
- const { body } = await deleteTemplates([templateName]).expect(200);
+ const { body } = await deleteTemplates([
+ { name: templateName, formatVersion: payload._kbnMeta.formatVersion },
+ ]).expect(200);
expect(body.errors).to.be.empty;
expect(body.templatesDeleted[0]).to.equal(templateName);
From 74dfe8340a0c37cdf708696ab72e398a6842333b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Mon, 30 Mar 2020 16:52:04 +0200
Subject: [PATCH 21/31] Fix component integration tests
---
.../__jest__/client_integration/home.test.ts | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts
index 440d8213867c8..a987535e0c291 100644
--- a/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts
+++ b/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts
@@ -304,7 +304,10 @@ describe('', () => {
const templateId = rows[0].columns[2].value;
- const { name: templateName } = template1;
+ const {
+ name: templateName,
+ _kbnMeta: { formatVersion },
+ } = template1;
await actions.clickTemplateAction(templateName, 'delete');
const modal = document.body.querySelector(
@@ -329,8 +332,11 @@ describe('', () => {
const latestRequest = server.requests[server.requests.length - 1];
- expect(latestRequest.method).toBe('DELETE');
- expect(latestRequest.url).toBe(`${API_BASE_PATH}/templates/${template1.name}`);
+ expect(latestRequest.method).toBe('POST');
+ expect(latestRequest.url).toBe(`${API_BASE_PATH}/delete-templates`);
+ expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual({
+ templates: [{ name: template1.name, formatVersion }],
+ });
});
});
From e9eff3360db8b34fa8e4c49a9ede35fbd124d5c0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Mon, 30 Mar 2020 17:44:35 +0200
Subject: [PATCH 22/31] Small refactor
---
.../__jest__/client_integration/template_edit.test.tsx | 2 +-
x-pack/plugins/index_management/common/index.ts | 2 +-
x-pack/plugins/index_management/common/lib/index.ts | 2 +-
.../common/lib/template_serialization.ts | 2 +-
x-pack/plugins/index_management/common/lib/utils.ts | 2 +-
.../plugins/index_management/common/types/templates.ts | 8 ++------
.../components/template_form/steps/step_review.tsx | 8 ++++----
.../components/template_form/template_form.tsx | 9 ++++++++-
8 files changed, 19 insertions(+), 16 deletions(-)
diff --git a/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx
index b28e18f977ef1..5153304406e3f 100644
--- a/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx
+++ b/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx
@@ -208,7 +208,7 @@ describe('', () => {
},
isManaged: false,
_kbnMeta: {
- formatVersion: 1,
+ formatVersion: templateToEdit._kbnMeta.formatVersion,
},
};
diff --git a/x-pack/plugins/index_management/common/index.ts b/x-pack/plugins/index_management/common/index.ts
index d9079e7d3bbf2..459eda7552c85 100644
--- a/x-pack/plugins/index_management/common/index.ts
+++ b/x-pack/plugins/index_management/common/index.ts
@@ -6,6 +6,6 @@
export { PLUGIN, API_BASE_PATH, DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT } from './constants';
-export { getTemplateSetting } from './lib';
+export { getTemplateParameter } from './lib';
export * from './types';
diff --git a/x-pack/plugins/index_management/common/lib/index.ts b/x-pack/plugins/index_management/common/lib/index.ts
index 7b338b911bf06..33f7fbe45182e 100644
--- a/x-pack/plugins/index_management/common/lib/index.ts
+++ b/x-pack/plugins/index_management/common/lib/index.ts
@@ -9,4 +9,4 @@ export {
serializeV1Template,
} from './template_serialization';
-export { getTemplateSetting } from './utils';
+export { getTemplateParameter } from './utils';
diff --git a/x-pack/plugins/index_management/common/lib/template_serialization.ts b/x-pack/plugins/index_management/common/lib/template_serialization.ts
index 6331c997d4cc3..9f9b6e1b59f5b 100644
--- a/x-pack/plugins/index_management/common/lib/template_serialization.ts
+++ b/x-pack/plugins/index_management/common/lib/template_serialization.ts
@@ -75,7 +75,7 @@ export function deserializeV2Template(
priority,
composedOf,
_kbnMeta: {
- formatVersion: 1,
+ formatVersion: 2,
},
};
diff --git a/x-pack/plugins/index_management/common/lib/utils.ts b/x-pack/plugins/index_management/common/lib/utils.ts
index 60eecfa5980be..eee35dc1ab467 100644
--- a/x-pack/plugins/index_management/common/lib/utils.ts
+++ b/x-pack/plugins/index_management/common/lib/utils.ts
@@ -17,7 +17,7 @@ export const getTemplateVersion = (
return {}.hasOwnProperty.call(template, 'template') ? 2 : 1;
};
-export const getTemplateSetting = (
+export const getTemplateParameter = (
template: TemplateV1Serialized | TemplateV2Serialized,
setting: 'aliases' | 'settings' | 'mappings'
) => {
diff --git a/x-pack/plugins/index_management/common/types/templates.ts b/x-pack/plugins/index_management/common/types/templates.ts
index 51047ee1042ae..c37088982f207 100644
--- a/x-pack/plugins/index_management/common/types/templates.ts
+++ b/x-pack/plugins/index_management/common/types/templates.ts
@@ -22,17 +22,13 @@ export interface TemplateV1Serialized extends TemplateBaseSerialized {
mappings?: Mappings;
}
-export interface TemplateV2Serialized {
- name: string;
- index_patterns: string[];
- version?: number;
- priority?: number;
- order?: number;
+export interface TemplateV2Serialized extends TemplateBaseSerialized {
template: {
settings?: IndexSettings;
aliases?: Aliases;
mappings?: Mappings;
};
+ priority?: number;
composed_of?: string[];
}
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
index 05a173b571937..0cb2ae9fbcd92 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
@@ -26,7 +26,7 @@ import {
serializeV1Template,
serializeV2Template,
} from '../../../../../common/lib/template_serialization';
-import { TemplateDeserialized, getTemplateSetting } from '../../../../../common';
+import { TemplateDeserialized, getTemplateParameter } from '../../../../../common';
import { StepProps } from '../types';
const { stripEmptyFields } = serializers;
@@ -71,9 +71,9 @@ export const StepReview: React.FunctionComponent = ({ template, updat
// Name not included in ES request body
delete serializedTemplate.name;
- const serializedMappings = getTemplateSetting(serializedTemplate, 'mappings');
- const serializedSettings = getTemplateSetting(serializedTemplate, 'settings');
- const serializedAliases = getTemplateSetting(serializedTemplate, 'aliases');
+ const serializedMappings = getTemplateParameter(serializedTemplate, 'mappings');
+ const serializedSettings = getTemplateParameter(serializedTemplate, 'settings');
+ const serializedAliases = getTemplateParameter(serializedTemplate, 'aliases');
const numIndexPatterns = indexPatterns!.length;
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
index bf0effef8544f..f6193bc71aa91 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
@@ -110,9 +110,16 @@ export const TemplateForm: React.FunctionComponent = ({
if (isValid) {
// Update the template object with the current step data
if (path) {
+ // We only update a "slice" of the template
+ const sliceToUpdate = template.current[path as keyof TemplateDeserialized];
+
+ if (sliceToUpdate === null || typeof sliceToUpdate !== 'object') {
+ return { isValid, data };
+ }
+
template.current = {
...template.current,
- [path]: { ...template.current[path as 'template'], ...data },
+ [path]: { ...sliceToUpdate, ...data },
};
} else {
template.current = { ...template.current, ...data };
From f1a91a887ab4375e5057237f618a046998c7d993 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Tue, 31 Mar 2020 11:37:06 +0200
Subject: [PATCH 23/31] Fix TS issue
---
.../index_management/common/lib/template_serialization.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/x-pack/plugins/index_management/common/lib/template_serialization.ts b/x-pack/plugins/index_management/common/lib/template_serialization.ts
index 9f9b6e1b59f5b..33a83d1e9335b 100644
--- a/x-pack/plugins/index_management/common/lib/template_serialization.ts
+++ b/x-pack/plugins/index_management/common/lib/template_serialization.ts
@@ -18,7 +18,7 @@ export function serializeV1Template(template: TemplateDeserialized): TemplateV1S
version,
order,
indexPatterns,
- template: { settings, aliases, mappings } = {},
+ template: { settings, aliases, mappings } = {} as TemplateDeserialized['template'],
} = template;
const serializedTemplate: TemplateV1Serialized = {
From b7946305508fb452a41184dcfacce57dc14fdd91 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Tue, 31 Mar 2020 15:18:53 +0200
Subject: [PATCH 24/31] Add some latency to component integration test
---
.../__jest__/client_integration/template_edit.test.tsx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx
index 5153304406e3f..c158f6f903c8b 100644
--- a/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx
+++ b/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx
@@ -167,6 +167,8 @@ describe('', () => {
// Step 4 (aliases)
await actions.completeStepFour(JSON.stringify(ALIASES));
+ await nextTick(50);
+ component.update();
});
});
From 41efbc3941f09571ae7e9eaf9446019b99c737d5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Wed, 1 Apr 2020 13:42:22 +0200
Subject: [PATCH 25/31] [TestBed] Add "waitFor" helper for flaxy tests
---
x-pack/test_utils/testbed/testbed.ts | 27 +++++++++++++++++++++++++++
x-pack/test_utils/testbed/types.ts | 6 ++++++
2 files changed, 33 insertions(+)
diff --git a/x-pack/test_utils/testbed/testbed.ts b/x-pack/test_utils/testbed/testbed.ts
index f32fb42a8a8b0..1dcbff613ebd7 100644
--- a/x-pack/test_utils/testbed/testbed.ts
+++ b/x-pack/test_utils/testbed/testbed.ts
@@ -138,6 +138,32 @@ export const registerTestBed = (
});
};
+ const waitFor: TestBed['waitFor'] = async (testSubject: T) => {
+ const triggeredAt = Date.now();
+ const MAX_WAIT_TIME = 2000;
+ const WAIT_INTERVAL = 50;
+
+ const process = async (): Promise => {
+ const elemFound = exists(testSubject);
+
+ if (!elemFound) {
+ const now = Date.now();
+ if (now - triggeredAt > MAX_WAIT_TIME) {
+ throw new Error(
+ `I waited patiently for the "${testSubject}" test subject to appear with no luck. It is nowhere to be found!`
+ );
+ }
+
+ return new Promise(resolve => setTimeout(resolve, WAIT_INTERVAL)).then(() => {
+ component.update();
+ return process();
+ });
+ }
+ };
+
+ return process();
+ };
+
/**
* ----------------------------------------------------------------
* Forms
@@ -254,6 +280,7 @@ export const registerTestBed = (
exists,
find,
setProps,
+ waitFor,
table: {
getMetaData,
},
diff --git a/x-pack/test_utils/testbed/types.ts b/x-pack/test_utils/testbed/types.ts
index c51e6a256f66f..e4bb3ee57adec 100644
--- a/x-pack/test_utils/testbed/types.ts
+++ b/x-pack/test_utils/testbed/types.ts
@@ -55,6 +55,12 @@ export interface TestBed {
* @param updatedProps The updated prop object
*/
setProps: (updatedProps: any) => void;
+ /**
+ * Helper to wait until an element appears in the DOM as hooks updates cycles are tricky.
+ * Useful when loading a component that fetches a resource from the server
+ * and we need to wait for the data to be fetched (and bypass any "loading" state).
+ */
+ waitFor: (testSubject: T) => Promise;
form: {
/**
* Set the value of a form text input.
From b0f193c53091b0b66ef593e10473a676b47efd40 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Wed, 1 Apr 2020 13:43:00 +0200
Subject: [PATCH 26/31] Make CR suggestions
---
.../index_management/common/constants/index_templates.ts | 2 +-
.../components/template_form/steps/use_json_step.ts | 2 +-
.../public/application/lib/index_templates.ts | 6 +++---
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/x-pack/plugins/index_management/common/constants/index_templates.ts b/x-pack/plugins/index_management/common/constants/index_templates.ts
index 27f9ec7a45ed1..788e96ee895ed 100644
--- a/x-pack/plugins/index_management/common/constants/index_templates.ts
+++ b/x-pack/plugins/index_management/common/constants/index_templates.ts
@@ -6,7 +6,7 @@
/**
* Up until the end of the 8.x release cycle we need to support both
- * V1 and V2 index template formats. This constant keeps track of wether
+ * V1 and V2 index template formats. This constant keeps track of whether
* we create V1 or V2 index template format in the UI.
*/
export const DEFAULT_INDEX_TEMPLATE_VERSION_FORMAT = 1;
diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts b/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts
index 1efa86243ab89..25dbe784db3a1 100644
--- a/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts
+++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts
@@ -47,7 +47,7 @@ export const useJsonStep = ({
const dataGetter = useCallback(() => {
const isValid = validateContent();
const value = isValid && content.trim() !== '' ? JSON.parse(content) : {};
- // If no key has been added to the JSON object, we strip it out so an empty objecct is not sent in the request
+ // If no key has been added to the JSON object, we strip it out so an empty object is not sent in the request
const data = { [prop]: Object.keys(value).length > 0 ? value : undefined };
return Promise.resolve({ isValid, data, path: 'template' });
diff --git a/x-pack/plugins/index_management/public/application/lib/index_templates.ts b/x-pack/plugins/index_management/public/application/lib/index_templates.ts
index ab02ceea1030c..7129e536287c1 100644
--- a/x-pack/plugins/index_management/public/application/lib/index_templates.ts
+++ b/x-pack/plugins/index_management/public/application/lib/index_templates.ts
@@ -7,11 +7,11 @@ import { parse } from 'query-string';
import { Location } from 'history';
export const getFormatVersionFromQueryparams = (location: Location): 1 | 2 | undefined => {
- const { v } = parse(location.search.substring(1));
+ const { v: version } = parse(location.search.substring(1));
- if (!Boolean(v) || typeof v !== 'string') {
+ if (!Boolean(version) || typeof version !== 'string') {
return undefined;
}
- return +v as 1 | 2;
+ return +version as 1 | 2;
};
From ede49bf0141457ee416914f68b615e905a8a0733 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Wed, 1 Apr 2020 13:43:44 +0200
Subject: [PATCH 27/31] [Form lib] Fix FormDataProvider initial state
---
.../forms/hook_form_lib/components/form_data_provider.ts | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts
index a8d24984cec7c..0509b8081c35b 100644
--- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts
+++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts
@@ -28,9 +28,9 @@ interface Props {
}
export const FormDataProvider = React.memo(({ children, pathsToWatch }: Props) => {
- const [formData, setFormData] = useState({});
- const previousRawData = useRef({});
const form = useFormContext();
+ const previousRawData = useRef(form.__formData$.current.value);
+ const [formData, setFormData] = useState(previousRawData.current);
useEffect(() => {
const subscription = form.subscribe(({ data: { raw } }) => {
@@ -41,6 +41,7 @@ export const FormDataProvider = React.memo(({ children, pathsToWatch }: Props) =
const valuesToWatchArray = Array.isArray(pathsToWatch)
? (pathsToWatch as string[])
: ([pathsToWatch] as string[]);
+
if (valuesToWatchArray.some(value => previousRawData.current[value] !== raw[value])) {
previousRawData.current = raw;
setFormData(raw);
From 0f124702582c53aab28e960f86c900b9447eafe8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Wed, 1 Apr 2020 13:45:37 +0200
Subject: [PATCH 28/31] Fix flaxy component integration test
---
.../helpers/template_form.helpers.ts | 25 +++++-------
.../client_integration/template_edit.test.tsx | 38 +++++++++----------
2 files changed, 27 insertions(+), 36 deletions(-)
diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts
index 9d62ed6bdbe5f..e158cdd6f51f5 100644
--- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts
+++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts
@@ -63,7 +63,7 @@ export const formSetup = async (initTestBed: SetupFunc) => {
order,
version,
}: Partial = {}) => {
- const { form, find, component } = testBed;
+ const { form, find, waitFor } = testBed;
if (name) {
form.setInputValue('nameField.input', name);
@@ -88,12 +88,11 @@ export const formSetup = async (initTestBed: SetupFunc) => {
}
clickNextButton();
- await nextTick();
- component.update();
+ await waitFor('stepSettings');
};
const completeStepTwo = async (settings?: string) => {
- const { find, component } = testBed;
+ const { find, component, waitFor } = testBed;
if (settings) {
find('mockCodeEditor').simulate('change', {
@@ -104,42 +103,36 @@ export const formSetup = async (initTestBed: SetupFunc) => {
}
clickNextButton();
- await nextTick();
- component.update();
+ await waitFor('stepMappings');
};
const completeStepThree = async (mappingFields?: MappingField[]) => {
- const { component } = testBed;
+ const { waitFor } = testBed;
if (mappingFields) {
for (const field of mappingFields) {
const { name, type } = field;
await addMappingField(name, type);
}
- } else {
- await nextTick();
}
- await nextTick(50); // hooks updates cycles are tricky, adding some latency is needed
clickNextButton();
- await nextTick(50);
- component.update();
+ await waitFor('stepAliases');
};
const completeStepFour = async (aliases?: string) => {
- const { find, component } = testBed;
+ const { find, component, waitFor } = testBed;
if (aliases) {
find('mockCodeEditor').simulate('change', {
jsonString: aliases,
}); // Using mocked EuiCodeEditor
- await nextTick(50);
+ await nextTick();
component.update();
}
clickNextButton();
- await nextTick(50);
- component.update();
+ await waitFor('summaryTab');
};
const selectSummaryTab = (tab: 'summary' | 'request') => {
diff --git a/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx
index c158f6f903c8b..5b10ff226022d 100644
--- a/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx
+++ b/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx
@@ -107,11 +107,9 @@ describe('', () => {
beforeEach(async () => {
httpRequestsMockHelpers.setLoadTemplateResponse(templateToEdit);
- testBed = await setup();
-
await act(async () => {
- await nextTick();
- testBed.component.update();
+ testBed = await setup();
+ await testBed.waitFor('templateForm');
});
});
@@ -132,7 +130,7 @@ describe('', () => {
describe('form payload', () => {
beforeEach(async () => {
- const { actions, component, find, form } = testBed;
+ const { actions } = testBed;
await act(async () => {
// Complete step 1 (logistics)
@@ -142,17 +140,25 @@ describe('', () => {
// Step 2 (index settings)
await actions.completeStepTwo(JSON.stringify(SETTINGS));
+ });
+ });
+
+ it('should send the correct payload with changed values', async () => {
+ const { actions, component, find, form } = testBed;
+
+ await act(async () => {
+ // Make some changes to the mappings (step 3)
- // Step 3 (mappings)
- // Select the first field to edit
- actions.clickEditButtonAtField(0);
+ actions.clickEditButtonAtField(0); // Select the first field to edit
await nextTick();
component.update();
+ });
- // verify edit field flyout
- expect(find('mappingsEditorFieldEdit').length).toEqual(1);
+ // verify edit field flyout
+ expect(find('mappingsEditorFieldEdit').length).toEqual(1);
- // change field name
+ await act(async () => {
+ // change the field name
form.setInputValue('nameParameterInput', UPDATED_MAPPING_TEXT_FIELD_NAME);
// Save changes
@@ -167,21 +173,13 @@ describe('', () => {
// Step 4 (aliases)
await actions.completeStepFour(JSON.stringify(ALIASES));
- await nextTick(50);
- component.update();
- });
- });
-
- it('should send the correct payload with changed values', async () => {
- const { actions } = testBed;
- await act(async () => {
+ // Submit the form
actions.clickSubmitButton();
await nextTick();
});
const latestRequest = server.requests[server.requests.length - 1];
-
const { version, order } = templateToEdit;
const expected = {
From d2ffe6ce09402c7a870b2925f62d7f3c7629aed8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Wed, 1 Apr 2020 18:04:18 +0200
Subject: [PATCH 29/31] Fix component integration tests
---
.../client_integration/helpers/template_form.helpers.ts | 9 +++++++--
.../__jest__/client_integration/template_clone.test.tsx | 6 ++----
.../__jest__/client_integration/template_create.test.tsx | 8 ++++++--
.../components/mappings_editor/mappings_state.tsx | 3 ++-
x-pack/test_utils/testbed/testbed.ts | 2 +-
5 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts
index e158cdd6f51f5..520b62083e7d3 100644
--- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts
+++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts
@@ -120,7 +120,7 @@ export const formSetup = async (initTestBed: SetupFunc) => {
await waitFor('stepAliases');
};
- const completeStepFour = async (aliases?: string) => {
+ const completeStepFour = async (aliases?: string, waitForNextStep = true) => {
const { find, component, waitFor } = testBed;
if (aliases) {
@@ -132,7 +132,12 @@ export const formSetup = async (initTestBed: SetupFunc) => {
}
clickNextButton();
- await waitFor('summaryTab');
+
+ if (waitForNextStep) {
+ await waitFor('summaryTab');
+ } else {
+ component.update();
+ }
};
const selectSummaryTab = (tab: 'summary' | 'request') => {
diff --git a/x-pack/plugins/index_management/__jest__/client_integration/template_clone.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_clone.test.tsx
index f95dbe49b53da..17e19bf881dee 100644
--- a/x-pack/plugins/index_management/__jest__/client_integration/template_clone.test.tsx
+++ b/x-pack/plugins/index_management/__jest__/client_integration/template_clone.test.tsx
@@ -62,11 +62,9 @@ describe('', () => {
beforeEach(async () => {
httpRequestsMockHelpers.setLoadTemplateResponse(templateToClone);
- testBed = await setup();
-
await act(async () => {
- await nextTick();
- testBed.component.update();
+ testBed = await setup();
+ await testBed.waitFor('templateForm');
});
});
diff --git a/x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx
index 68c46af89af9b..ad8e8c22a87fa 100644
--- a/x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx
+++ b/x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx
@@ -72,6 +72,7 @@ describe('', () => {
beforeEach(async () => {
await act(async () => {
testBed = await setup();
+ await testBed.waitFor('templateForm');
});
});
@@ -101,6 +102,7 @@ describe('', () => {
beforeEach(async () => {
await act(async () => {
testBed = await setup();
+ await testBed.waitFor('templateForm');
});
});
@@ -210,7 +212,7 @@ describe('', () => {
await act(async () => {
// Complete step 4 (aliases) with invalid json
- await actions.completeStepFour('{ invalidJsonString ');
+ await actions.completeStepFour('{ invalidJsonString ', false);
});
expect(form.getErrorsMessages()).toContain('Invalid JSON format.');
@@ -222,6 +224,7 @@ describe('', () => {
beforeEach(async () => {
await act(async () => {
testBed = await setup();
+ await testBed.waitFor('templateForm');
const { actions } = testBed;
@@ -276,6 +279,7 @@ describe('', () => {
it('should render a warning message if a wildcard is used as an index pattern', async () => {
await act(async () => {
testBed = await setup();
+ await testBed.waitFor('templateForm');
const { actions } = testBed;
// Complete step 1 (logistics)
@@ -309,6 +313,7 @@ describe('', () => {
await act(async () => {
testBed = await setup();
+ await testBed.waitFor('templateForm');
const { actions } = testBed;
// Complete step 1 (logistics)
@@ -324,7 +329,6 @@ describe('', () => {
await actions.completeStepThree(MAPPING_FIELDS);
// Complete step 4 (aliases)
- await nextTick(100);
await actions.completeStepFour(JSON.stringify(ALIASES));
});
});
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx
index 247bd183baddf..a9d26b953b96e 100644
--- a/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx
@@ -111,13 +111,14 @@ export const MappingsState = React.memo(({ children, onUpdate, defaultValue }: P
let nextState = state;
if (
+ state.fieldForm &&
state.documentFields.status === 'creatingField' &&
isValid &&
!bypassFieldFormValidation
) {
// If the form field is valid and we are creating a new field that has some data
// we automatically add the field to our state.
- const fieldFormData = state.fieldForm!.data.format() as Field;
+ const fieldFormData = state.fieldForm.data.format() as Field;
if (Object.keys(fieldFormData).length !== 0) {
nextState = addFieldToState(fieldFormData, state);
dispatch({ type: 'field.add', value: fieldFormData });
diff --git a/x-pack/test_utils/testbed/testbed.ts b/x-pack/test_utils/testbed/testbed.ts
index 1dcbff613ebd7..14727407fd2ff 100644
--- a/x-pack/test_utils/testbed/testbed.ts
+++ b/x-pack/test_utils/testbed/testbed.ts
@@ -140,7 +140,7 @@ export const registerTestBed = (
const waitFor: TestBed['waitFor'] = async (testSubject: T) => {
const triggeredAt = Date.now();
- const MAX_WAIT_TIME = 2000;
+ const MAX_WAIT_TIME = 15000;
const WAIT_INTERVAL = 50;
const process = async (): Promise => {
From 4b2c9bbab7b6762092ae47522dff1934e3c2db8d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Wed, 1 Apr 2020 18:17:02 +0200
Subject: [PATCH 30/31] Fix flaky test for
---
.../load_mappings/load_mappings_provider.test.tsx | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx
index b66a42fc50ef4..da4b8e6f6eef2 100644
--- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx
@@ -38,22 +38,20 @@ const setup = (props: any) =>
defaultProps: props,
})();
-const openModalWithJsonContent = ({ find, component }: TestBed) => async (json: any) => {
- find('load-json-button').simulate('click');
- component.update();
-
+const openModalWithJsonContent = ({ find, waitFor }: TestBed) => async (json: any) => {
// Set the mappings to load
- // @ts-ignore
await act(async () => {
+ find('load-json-button').simulate('click');
+ await waitFor('mockCodeEditor');
+
find('mockCodeEditor').simulate('change', {
jsonString: JSON.stringify(json),
});
- await nextTick(300); // There is a debounce in the JsonEditor that we need to wait for
+ await nextTick(500); // There is a debounce in the JsonEditor that we need to wait for
});
};
-// FLAKY: https://github.com/elastic/kibana/issues/59030
-describe.skip('', () => {
+describe('', () => {
test('it should forward valid mapping definition', async () => {
const mappingsToLoad = {
properties: {
From 6e95fd71a52eadc6c23216b291da8ed87fd5d7a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Se=CC=81bastien=20Loix?=
Date: Thu, 2 Apr 2020 15:09:00 +0200
Subject: [PATCH 31/31] Refactor TestBed.waitFor() and wait up to 30 sec
---
x-pack/test_utils/testbed/testbed.ts | 40 ++++++++++++++++++----------
1 file changed, 26 insertions(+), 14 deletions(-)
diff --git a/x-pack/test_utils/testbed/testbed.ts b/x-pack/test_utils/testbed/testbed.ts
index 14727407fd2ff..2bd53adda2cdf 100644
--- a/x-pack/test_utils/testbed/testbed.ts
+++ b/x-pack/test_utils/testbed/testbed.ts
@@ -140,25 +140,37 @@ export const registerTestBed = (
const waitFor: TestBed['waitFor'] = async (testSubject: T) => {
const triggeredAt = Date.now();
- const MAX_WAIT_TIME = 15000;
- const WAIT_INTERVAL = 50;
+
+ /**
+ * The way jest run tests in parallel + the not deterministic DOM update from React "hooks"
+ * add flakiness to the tests. This is especially true for component integration tests that
+ * make many update to the DOM.
+ *
+ * For this reason, when we _know_ that an element should be there after we updated some state,
+ * we will give it 30 seconds to appear in the DOM, checking every 100 ms for its presence.
+ */
+ const MAX_WAIT_TIME = 30000;
+ const WAIT_INTERVAL = 100;
const process = async (): Promise => {
const elemFound = exists(testSubject);
- if (!elemFound) {
- const now = Date.now();
- if (now - triggeredAt > MAX_WAIT_TIME) {
- throw new Error(
- `I waited patiently for the "${testSubject}" test subject to appear with no luck. It is nowhere to be found!`
- );
- }
-
- return new Promise(resolve => setTimeout(resolve, WAIT_INTERVAL)).then(() => {
- component.update();
- return process();
- });
+ if (elemFound) {
+ // Great! nothing else to do here.
+ return;
+ }
+
+ const timeElapsed = Date.now() - triggeredAt;
+ if (timeElapsed > MAX_WAIT_TIME) {
+ throw new Error(
+ `I waited patiently for the "${testSubject}" test subject to appear with no luck. It is nowhere to be found!`
+ );
}
+
+ return new Promise(resolve => setTimeout(resolve, WAIT_INTERVAL)).then(() => {
+ component.update();
+ return process();
+ });
};
return process();