(
diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/index.ts b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/index.ts
index 58db8af3f7c5c..a796a2d1124b4 100644
--- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/index.ts
+++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/index.ts
@@ -11,3 +11,5 @@ export * from './mappings_editor';
export * from './components/load_mappings';
export { OnUpdateHandler, Types } from './mappings_state';
+
+export { doMappingsHaveType } from './lib';
diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/extract_mappings_definition.test.ts b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/extract_mappings_definition.test.ts
index 3e38ff5991a8c..cf399f55e660e 100644
--- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/extract_mappings_definition.test.ts
+++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/extract_mappings_definition.test.ts
@@ -73,7 +73,10 @@ describe('extractMappingsDefinition', () => {
},
};
- expect(extractMappingsDefinition(mappings)).toBe(mappings.type2);
+ expect(extractMappingsDefinition(mappings)).toEqual({
+ type: 'type2',
+ mappings: mappings.type2,
+ });
});
test('should detect that the mappings has one type and return its mapping definition', () => {
@@ -97,7 +100,37 @@ describe('extractMappingsDefinition', () => {
},
};
- expect(extractMappingsDefinition(mappings)).toBe(mappings.myType);
+ expect(extractMappingsDefinition(mappings)).toEqual({
+ type: 'myType',
+ mappings: mappings.myType,
+ });
+ });
+
+ test('should detect that the mappings has one custom type whose name matches a mappings definition parameter', () => {
+ const mappings = {
+ dynamic: {
+ _source: {
+ excludes: [],
+ includes: [],
+ enabled: true,
+ },
+ _meta: {},
+ _routing: {
+ required: false,
+ },
+ dynamic: true,
+ properties: {
+ title: {
+ type: 'keyword',
+ },
+ },
+ },
+ };
+
+ expect(extractMappingsDefinition(mappings)).toEqual({
+ type: 'dynamic',
+ mappings: mappings.dynamic,
+ });
});
test('should detect that the mappings has one type at root level', () => {
@@ -123,6 +156,6 @@ describe('extractMappingsDefinition', () => {
},
};
- expect(extractMappingsDefinition(mappings)).toBe(mappings);
+ expect(extractMappingsDefinition(mappings)).toEqual({ mappings });
});
});
diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/extract_mappings_definition.ts b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/extract_mappings_definition.ts
index eae3c5b15759c..9f2a3226b69e0 100644
--- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/extract_mappings_definition.ts
+++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/extract_mappings_definition.ts
@@ -6,15 +6,15 @@
import { isPlainObject } from 'lodash';
import { GenericObject } from '../types';
-import {
- validateMappingsConfiguration,
- mappingsConfigurationSchemaKeys,
-} from './mappings_validator';
+import { validateMappingsConfiguration, VALID_MAPPINGS_PARAMETERS } from './mappings_validator';
-const ALLOWED_PARAMETERS = [...mappingsConfigurationSchemaKeys, 'dynamic_templates', 'properties'];
+interface MappingsWithType {
+ type?: string;
+ mappings: GenericObject;
+}
const isMappingDefinition = (obj: GenericObject): boolean => {
- const areAllKeysValid = Object.keys(obj).every(key => ALLOWED_PARAMETERS.includes(key));
+ const areAllKeysValid = Object.keys(obj).every(key => VALID_MAPPINGS_PARAMETERS.includes(key));
if (!areAllKeysValid) {
return false;
@@ -32,6 +32,29 @@ const isMappingDefinition = (obj: GenericObject): boolean => {
return isConfigurationValid && isPropertiesValid && isDynamicTemplatesValid;
};
+const getMappingsDefinitionWithType = (mappings: GenericObject): MappingsWithType[] => {
+ if (isMappingDefinition(mappings)) {
+ // No need to go any further
+ return [{ mappings }];
+ }
+
+ // At this point there must be one or more type mappings
+ const typedMappings = Object.entries(mappings).reduce(
+ (acc: Array<{ type: string; mappings: GenericObject }>, [type, value]) => {
+ if (isMappingDefinition(value)) {
+ acc.push({ type, mappings: value as GenericObject });
+ }
+ return acc;
+ },
+ []
+ );
+
+ return typedMappings;
+};
+
+export const doMappingsHaveType = (mappings: GenericObject = {}): boolean =>
+ getMappingsDefinitionWithType(mappings).filter(({ type }) => type !== undefined).length > 0;
+
/**
* 5.x index templates can be created with multiple types.
* e.g.
@@ -72,19 +95,10 @@ const isMappingDefinition = (obj: GenericObject): boolean => {
*
* @param mappings The mappings object to validate
*/
-export const extractMappingsDefinition = (mappings: GenericObject = {}): GenericObject | null => {
- if (isMappingDefinition(mappings)) {
- // No need to go any further
- return mappings;
- }
-
- // At this point there must be one or more type mappings
- const typedMappings = Object.values(mappings).reduce((acc: GenericObject[], value) => {
- if (isMappingDefinition(value)) {
- acc.push(value as GenericObject);
- }
- return acc;
- }, []);
+export const extractMappingsDefinition = (
+ mappings: GenericObject = {}
+): MappingsWithType | null => {
+ const typedMappings = getMappingsDefinitionWithType(mappings);
// If there are no typed mappings found this means that one of the type must did not pass
// the "isMappingDefinition()" validation.
diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.test.ts b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.test.ts
index f1e6efb06c649..d67c267dda6ae 100644
--- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.test.ts
+++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.test.ts
@@ -18,6 +18,24 @@ describe('Mappings configuration validator', () => {
});
});
+ it('should detect valid mappings configuration', () => {
+ const mappings = {
+ _source: {
+ includes: [],
+ excludes: [],
+ enabled: true,
+ },
+ _meta: {},
+ _routing: {
+ required: false,
+ },
+ dynamic: true,
+ };
+
+ const { errors } = validateMappings(mappings);
+ expect(errors).toBe(undefined);
+ });
+
it('should strip out unknown configuration', () => {
const mappings = {
dynamic: true,
@@ -30,6 +48,7 @@ describe('Mappings configuration validator', () => {
excludes: ['abc'],
},
properties: { title: { type: 'text' } },
+ dynamic_templates: [],
unknown: 123,
};
@@ -37,7 +56,7 @@ describe('Mappings configuration validator', () => {
const { unknown, ...expected } = mappings;
expect(value).toEqual(expected);
- expect(errors).toBe(undefined);
+ expect(errors).toEqual([{ code: 'ERR_CONFIG', configName: 'unknown' }]);
});
it('should strip out invalid configuration and returns the errors for each of them', () => {
@@ -47,9 +66,8 @@ describe('Mappings configuration validator', () => {
dynamic_date_formats: false, // wrong format
_source: {
enabled: true,
- includes: 'abc',
+ unknownProp: 'abc', // invalid
excludes: ['abc'],
- wrong: 123, // parameter not allowed
},
properties: 'abc',
};
@@ -59,10 +77,10 @@ describe('Mappings configuration validator', () => {
expect(value).toEqual({
dynamic: true,
properties: {},
+ dynamic_templates: [],
});
expect(errors).not.toBe(undefined);
- expect(errors!.length).toBe(3);
expect(errors!).toEqual([
{ code: 'ERR_CONFIG', configName: '_source' },
{ code: 'ERR_CONFIG', configName: 'dynamic_date_formats' },
diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.ts b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.ts
index 6ccbfeb50dcf4..78d638e398593 100644
--- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.ts
+++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.ts
@@ -196,23 +196,30 @@ export const validateProperties = (properties = {}): PropertiesValidatorResponse
* Single source of truth to validate the *configuration* of the mappings.
* Whenever a user loads a JSON object it will be validate against this Joi schema.
*/
-export const mappingsConfigurationSchema = t.partial({
- dynamic: t.union([t.literal(true), t.literal(false), t.literal('strict')]),
- date_detection: t.boolean,
- numeric_detection: t.boolean,
- dynamic_date_formats: t.array(t.string),
- _source: t.partial({
- enabled: t.boolean,
- includes: t.array(t.string),
- excludes: t.array(t.string),
- }),
- _meta: t.UnknownRecord,
- _routing: t.partial({
- required: t.boolean,
- }),
-});
-
-export const mappingsConfigurationSchemaKeys = Object.keys(mappingsConfigurationSchema.props);
+export const mappingsConfigurationSchema = t.exact(
+ t.partial({
+ dynamic: t.union([t.literal(true), t.literal(false), t.literal('strict')]),
+ date_detection: t.boolean,
+ numeric_detection: t.boolean,
+ dynamic_date_formats: t.array(t.string),
+ _source: t.exact(
+ t.partial({
+ enabled: t.boolean,
+ includes: t.array(t.string),
+ excludes: t.array(t.string),
+ })
+ ),
+ _meta: t.UnknownRecord,
+ _routing: t.interface({
+ required: t.boolean,
+ }),
+ })
+);
+
+const mappingsConfigurationSchemaKeys = Object.keys(mappingsConfigurationSchema.type.props);
+const sourceConfigurationSchemaKeys = Object.keys(
+ mappingsConfigurationSchema.type.props._source.type.props
+);
export const validateMappingsConfiguration = (
mappingsConfiguration: any
@@ -222,8 +229,20 @@ export const validateMappingsConfiguration = (
let copyOfMappingsConfig = { ...mappingsConfiguration };
const result = mappingsConfigurationSchema.decode(mappingsConfiguration);
+ const isSchemaInvalid = isLeft(result);
- if (isLeft(result)) {
+ const unknownConfigurationParameters = Object.keys(mappingsConfiguration).filter(
+ key => mappingsConfigurationSchemaKeys.includes(key) === false
+ );
+
+ const unknownSourceConfigurationParameters =
+ mappingsConfiguration._source !== undefined
+ ? Object.keys(mappingsConfiguration._source).filter(
+ key => sourceConfigurationSchemaKeys.includes(key) === false
+ )
+ : [];
+
+ if (isSchemaInvalid) {
/**
* To keep the logic simple we will strip out the parameters that contain errors
*/
@@ -235,6 +254,15 @@ export const validateMappingsConfiguration = (
});
}
+ if (unknownConfigurationParameters.length > 0) {
+ unknownConfigurationParameters.forEach(configName => configurationRemoved.add(configName));
+ }
+
+ if (unknownSourceConfigurationParameters.length > 0) {
+ configurationRemoved.add('_source');
+ delete copyOfMappingsConfig._source;
+ }
+
copyOfMappingsConfig = pick(copyOfMappingsConfig, mappingsConfigurationSchemaKeys);
const errors: MappingsValidationError[] = toArray(ordString)(configurationRemoved)
@@ -252,7 +280,7 @@ export const validateMappings = (mappings: any = {}): MappingsValidatorResponse
return { value: {} };
}
- const { properties, dynamic_templates, ...mappingsConfiguration } = mappings;
+ const { properties, dynamic_templates: dynamicTemplates, ...mappingsConfiguration } = mappings;
const { value: parsedConfiguration, errors: configurationErrors } = validateMappingsConfiguration(
mappingsConfiguration
@@ -265,8 +293,14 @@ export const validateMappings = (mappings: any = {}): MappingsValidatorResponse
value: {
...parsedConfiguration,
properties: parsedProperties,
- dynamic_templates,
+ dynamic_templates: dynamicTemplates ?? [],
},
errors: errors.length ? errors : undefined,
};
};
+
+export const VALID_MAPPINGS_PARAMETERS = [
+ ...mappingsConfigurationSchemaKeys,
+ 'dynamic_templates',
+ 'properties',
+];
diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_editor.tsx b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_editor.tsx
index d79a023386e8d..a5a1153f3ba82 100644
--- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_editor.tsx
+++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_editor.tsx
@@ -31,7 +31,7 @@ type TabName = 'fields' | 'advanced' | 'templates';
export const MappingsEditor = React.memo(({ onUpdate, defaultValue, indexSettings }: Props) => {
const [selectedTab, selectTab] = useState('fields');
- const { parsedDefaultValue, multipleMappingsDeclared } = useMemo(() => {
+ const { parsedDefaultValue, multipleMappingsDeclared, mappingsType } = useMemo(() => {
const mappingsDefinition = extractMappingsDefinition(defaultValue);
if (mappingsDefinition === null) {
@@ -48,7 +48,7 @@ export const MappingsEditor = React.memo(({ onUpdate, defaultValue, indexSetting
dynamic_date_formats,
properties = {},
dynamic_templates,
- } = mappingsDefinition;
+ } = mappingsDefinition.mappings;
const parsed = {
configuration: {
@@ -66,7 +66,11 @@ export const MappingsEditor = React.memo(({ onUpdate, defaultValue, indexSetting
},
};
- return { parsedDefaultValue: parsed, multipleMappingsDeclared: false };
+ return {
+ parsedDefaultValue: parsed,
+ multipleMappingsDeclared: false,
+ mappingsType: mappingsDefinition.type,
+ };
}, [defaultValue]);
useEffect(() => {
@@ -108,7 +112,11 @@ export const MappingsEditor = React.memo(({ onUpdate, defaultValue, indexSetting
) : (
-
+
{({ state }) => {
const tabToContentMap = {
fields: ,
diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_state.tsx b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_state.tsx
index 65a1aa2858d14..634fde393c2cd 100644
--- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_state.tsx
+++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_state.tsx
@@ -32,7 +32,7 @@ export interface Types {
export interface OnUpdateHandlerArg {
isValid?: boolean;
- getData: (isValid: boolean) => Mappings;
+ getData: (isValid: boolean) => Mappings | { [key: string]: Mappings };
validate: () => Promise;
}
@@ -49,152 +49,161 @@ export interface Props {
fields: { [key: string]: Field };
};
onUpdate: OnUpdateHandler;
+ mappingsType?: string;
}
-export const MappingsState = React.memo(({ children, onUpdate, defaultValue }: Props) => {
- const didMountRef = useRef(false);
-
- const parsedFieldsDefaultValue = useMemo(() => normalize(defaultValue.fields), [
- defaultValue.fields,
- ]);
-
- const initialState: State = {
- isValid: undefined,
- configuration: {
- defaultValue: defaultValue.configuration,
- data: {
- raw: defaultValue.configuration,
- format: () => defaultValue.configuration,
+export const MappingsState = React.memo(
+ ({ children, onUpdate, defaultValue, mappingsType }: Props) => {
+ const didMountRef = useRef(false);
+
+ const parsedFieldsDefaultValue = useMemo(() => normalize(defaultValue.fields), [
+ defaultValue.fields,
+ ]);
+
+ const initialState: State = {
+ isValid: undefined,
+ configuration: {
+ defaultValue: defaultValue.configuration,
+ data: {
+ raw: defaultValue.configuration,
+ format: () => defaultValue.configuration,
+ },
+ validate: () => Promise.resolve(true),
},
- validate: () => Promise.resolve(true),
- },
- templates: {
- defaultValue: defaultValue.templates,
- data: {
- raw: defaultValue.templates,
- format: () => defaultValue.templates,
+ templates: {
+ defaultValue: defaultValue.templates,
+ data: {
+ raw: defaultValue.templates,
+ format: () => defaultValue.templates,
+ },
+ validate: () => Promise.resolve(true),
},
- validate: () => Promise.resolve(true),
- },
- fields: parsedFieldsDefaultValue,
- documentFields: {
- status: 'idle',
- editor: 'default',
- },
- fieldsJsonEditor: {
- format: () => ({}),
- isValid: true,
- },
- search: {
- term: '',
- result: [],
- },
- };
-
- const [state, dispatch] = useReducer(reducer, initialState);
-
- useEffect(() => {
- // If we are creating a new field, but haven't entered any name
- // it is valid and we can byPass its form validation (that requires a "name" to be defined)
- const isFieldFormVisible = state.fieldForm !== undefined;
- const emptyNameValue =
- isFieldFormVisible &&
- state.fieldForm!.data.raw.name !== undefined &&
- state.fieldForm!.data.raw.name.trim() === '';
-
- const bypassFieldFormValidation =
- state.documentFields.status === 'creatingField' && emptyNameValue;
-
- onUpdate({
- // Output a mappings object from the user's input.
- getData: (isValid: boolean) => {
- let nextState = state;
-
- if (
- 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;
- if (Object.keys(fieldFormData).length !== 0) {
- nextState = addFieldToState(fieldFormData, state);
- dispatch({ type: 'field.add', value: fieldFormData });
- }
- }
-
- // Pull the mappings properties from the current editor
- const fields =
- nextState.documentFields.editor === 'json'
- ? nextState.fieldsJsonEditor.format()
- : deNormalize(nextState.fields);
-
- const configurationData = nextState.configuration.data.format();
- const templatesData = nextState.templates.data.format();
-
- return {
- ...configurationData,
- ...templatesData,
- properties: fields,
- };
+ fields: parsedFieldsDefaultValue,
+ documentFields: {
+ status: 'idle',
+ editor: 'default',
},
- validate: async () => {
- const configurationFormValidator =
- state.configuration.submitForm !== undefined
- ? new Promise(async resolve => {
- const { isValid } = await state.configuration.submitForm!();
- resolve(isValid);
- })
- : Promise.resolve(true);
-
- const templatesFormValidator =
- state.templates.submitForm !== undefined
- ? new Promise(async resolve => {
- const { isValid } = await state.templates.submitForm!();
- resolve(isValid);
- })
- : Promise.resolve(true);
-
- const promisesToValidate = [configurationFormValidator, templatesFormValidator];
-
- if (state.fieldForm !== undefined && !bypassFieldFormValidation) {
- promisesToValidate.push(state.fieldForm.validate());
- }
-
- return Promise.all(promisesToValidate).then(
- validationArray => validationArray.every(Boolean) && state.fieldsJsonEditor.isValid
- );
+ fieldsJsonEditor: {
+ format: () => ({}),
+ isValid: true,
},
- isValid: state.isValid,
- });
- }, [state]);
-
- useEffect(() => {
- /**
- * If the defaultValue has changed that probably means that we have loaded
- * new data from JSON. We need to update our state with the new mappings.
- */
- if (didMountRef.current) {
- dispatch({
- type: 'editor.replaceMappings',
- value: {
- configuration: defaultValue.configuration,
- templates: defaultValue.templates,
- fields: parsedFieldsDefaultValue,
+ search: {
+ term: '',
+ result: [],
+ },
+ };
+
+ const [state, dispatch] = useReducer(reducer, initialState);
+
+ useEffect(() => {
+ // If we are creating a new field, but haven't entered any name
+ // it is valid and we can byPass its form validation (that requires a "name" to be defined)
+ const isFieldFormVisible = state.fieldForm !== undefined;
+ const emptyNameValue =
+ isFieldFormVisible &&
+ state.fieldForm!.data.raw.name !== undefined &&
+ state.fieldForm!.data.raw.name.trim() === '';
+
+ const bypassFieldFormValidation =
+ state.documentFields.status === 'creatingField' && emptyNameValue;
+
+ onUpdate({
+ // Output a mappings object from the user's input.
+ getData: (isValid: boolean) => {
+ let nextState = state;
+
+ if (
+ 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;
+ if (Object.keys(fieldFormData).length !== 0) {
+ nextState = addFieldToState(fieldFormData, state);
+ dispatch({ type: 'field.add', value: fieldFormData });
+ }
+ }
+
+ // Pull the mappings properties from the current editor
+ const fields =
+ nextState.documentFields.editor === 'json'
+ ? nextState.fieldsJsonEditor.format()
+ : deNormalize(nextState.fields);
+
+ const configurationData = nextState.configuration.data.format();
+ const templatesData = nextState.templates.data.format();
+
+ const mappings = {
+ ...configurationData,
+ ...templatesData,
+ properties: fields,
+ };
+
+ return mappingsType === undefined
+ ? mappings
+ : {
+ [mappingsType]: mappings,
+ };
},
+ validate: async () => {
+ const configurationFormValidator =
+ state.configuration.submitForm !== undefined
+ ? new Promise(async resolve => {
+ const { isValid } = await state.configuration.submitForm!();
+ resolve(isValid);
+ })
+ : Promise.resolve(true);
+
+ const templatesFormValidator =
+ state.templates.submitForm !== undefined
+ ? new Promise(async resolve => {
+ const { isValid } = await state.templates.submitForm!();
+ resolve(isValid);
+ })
+ : Promise.resolve(true);
+
+ const promisesToValidate = [configurationFormValidator, templatesFormValidator];
+
+ if (state.fieldForm !== undefined && !bypassFieldFormValidation) {
+ promisesToValidate.push(state.fieldForm.validate());
+ }
+
+ return Promise.all(promisesToValidate).then(
+ validationArray => validationArray.every(Boolean) && state.fieldsJsonEditor.isValid
+ );
+ },
+ isValid: state.isValid,
});
- } else {
- didMountRef.current = true;
- }
- }, [defaultValue]);
-
- return (
-
- {children({ state })}
-
- );
-});
+ }, [state]);
+
+ useEffect(() => {
+ /**
+ * If the defaultValue has changed that probably means that we have loaded
+ * new data from JSON. We need to update our state with the new mappings.
+ */
+ if (didMountRef.current) {
+ dispatch({
+ type: 'editor.replaceMappings',
+ value: {
+ configuration: defaultValue.configuration,
+ templates: defaultValue.templates,
+ fields: parsedFieldsDefaultValue,
+ },
+ });
+ } else {
+ didMountRef.current = true;
+ }
+ }, [defaultValue]);
+
+ return (
+
+ {children({ state })}
+
+ );
+ }
+);
export const useMappingsState = () => {
const ctx = useContext(StateContext);
diff --git a/x-pack/legacy/plugins/index_management/public/app/services/api.ts b/x-pack/legacy/plugins/index_management/public/app/services/api.ts
index bdd5c39919646..d28047825af18 100644
--- a/x-pack/legacy/plugins/index_management/public/app/services/api.ts
+++ b/x-pack/legacy/plugins/index_management/public/app/services/api.ts
@@ -38,6 +38,7 @@ import { uiMetricService } from './ui_metric';
import { useRequest, sendRequest } from './use_request';
import { httpService } from './http';
import { Template } from '../../../common/types';
+import { doMappingsHaveType } from '../components/mappings_editor';
let httpClient: ng.IHttpService;
@@ -229,10 +230,14 @@ export function loadIndexTemplate(name: Template['name']) {
}
export async function saveTemplate(template: Template, isClone?: boolean) {
+ const includeTypeName = doMappingsHaveType(template.mappings);
const result = await sendRequest({
path: `${API_BASE_PATH}/templates`,
method: 'put',
body: JSON.stringify(template),
+ query: {
+ include_type_name: includeTypeName,
+ },
});
const uimActionType = isClone ? UIM_TEMPLATE_CLONE : UIM_TEMPLATE_CREATE;
@@ -243,11 +248,15 @@ export async function saveTemplate(template: Template, isClone?: boolean) {
}
export async function updateTemplate(template: Template) {
+ const includeTypeName = doMappingsHaveType(template.mappings);
const { name } = template;
const result = await sendRequest({
path: `${API_BASE_PATH}/templates/${encodeURIComponent(name)}`,
method: 'put',
body: JSON.stringify(template),
+ query: {
+ include_type_name: includeTypeName,
+ },
});
uiMetricService.trackMetric('count', UIM_TEMPLATE_UPDATE);
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/mapping/register_mapping_route.ts b/x-pack/legacy/plugins/index_management/server/routes/api/mapping/register_mapping_route.ts
index 86600aab76580..2f6530c69d938 100644
--- a/x-pack/legacy/plugins/index_management/server/routes/api/mapping/register_mapping_route.ts
+++ b/x-pack/legacy/plugins/index_management/server/routes/api/mapping/register_mapping_route.ts
@@ -17,6 +17,7 @@ const handler: RouterRouteHandler = async (request, callWithRequest) => {
const params = {
expand_wildcards: 'none',
index: indexName,
+ include_type_name: true,
};
const hit = await callWithRequest('indices.getMapping', params);
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_create_route.ts b/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_create_route.ts
index e134a97dd029e..8cc9f24f78a52 100644
--- a/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_create_route.ts
+++ b/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_create_route.ts
@@ -15,6 +15,7 @@ import { serializeTemplate } from '../../../../common/lib';
const handler: RouterRouteHandler = async (req, callWithRequest) => {
const template = req.payload as Template;
+ const { include_type_name } = req.query as any;
const serializedTemplate = serializeTemplate(template) as TemplateEs;
const { name, order, index_patterns, version, settings, mappings, aliases } = serializedTemplate;
@@ -49,6 +50,7 @@ const handler: RouterRouteHandler = async (req, callWithRequest) => {
return await callWithRequest('indices.putTemplate', {
name,
order,
+ include_type_name,
body: {
index_patterns,
version,
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_get_routes.ts b/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_get_routes.ts
index b450f75d1cc53..555feafa053d1 100644
--- a/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_get_routes.ts
+++ b/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_get_routes.ts
@@ -13,7 +13,9 @@ let callWithInternalUser: any;
const allHandler: RouterRouteHandler = async (_req, callWithRequest) => {
const managedTemplatePrefix = await getManagedTemplatePrefix(callWithInternalUser);
- const indexTemplatesByName = await callWithRequest('indices.getTemplate');
+ const indexTemplatesByName = await callWithRequest('indices.getTemplate', {
+ include_type_name: true,
+ });
return deserializeTemplateList(indexTemplatesByName, managedTemplatePrefix);
};
@@ -21,7 +23,10 @@ const allHandler: RouterRouteHandler = async (_req, callWithRequest) => {
const oneHandler: RouterRouteHandler = async (req, callWithRequest) => {
const { name } = req.params;
const managedTemplatePrefix = await getManagedTemplatePrefix(callWithInternalUser);
- const indexTemplateByName = await callWithRequest('indices.getTemplate', { name });
+ const indexTemplateByName = await callWithRequest('indices.getTemplate', {
+ name,
+ include_type_name: true,
+ });
if (indexTemplateByName[name]) {
return deserializeTemplate({ ...indexTemplateByName[name], name }, managedTemplatePrefix);
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_update_route.ts b/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_update_route.ts
index 15590e2acbe71..ccbda2dab9364 100644
--- a/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_update_route.ts
+++ b/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_update_route.ts
@@ -10,6 +10,7 @@ import { serializeTemplate } from '../../../../common/lib';
const handler: RouterRouteHandler = async (req, callWithRequest) => {
const { name } = req.params;
+ const { include_type_name } = req.query as any;
const template = req.payload as Template;
const serializedTemplate = serializeTemplate(template) as TemplateEs;
@@ -22,6 +23,7 @@ const handler: RouterRouteHandler = async (req, callWithRequest) => {
return await callWithRequest('indices.putTemplate', {
name,
order,
+ include_type_name,
body: {
index_patterns,
version,
diff --git a/x-pack/test/api_integration/apis/management/index_management/mapping.js b/x-pack/test/api_integration/apis/management/index_management/mapping.js
index fa0f6e04a7a4d..13eca5bef251a 100644
--- a/x-pack/test/api_integration/apis/management/index_management/mapping.js
+++ b/x-pack/test/api_integration/apis/management/index_management/mapping.js
@@ -32,7 +32,8 @@ export default function({ getService }) {
const { body } = await getIndexMapping(index).expect(200);
- expect(body.mapping).to.eql(mappings);
+ // As, on 7.x we require the mappings with type (include_type_name), the default "_doc" type is returned
+ expect(body.mapping).to.eql({ _doc: mappings });
});
});
}