Skip to content

Commit f8b0425

Browse files
committed
Don't send empty objects inside template
1 parent bb56efa commit f8b0425

File tree

10 files changed

+105
-51
lines changed

10 files changed

+105
-51
lines changed

x-pack/plugins/index_management/common/lib/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ export const getTemplateParameter = (
2323
) => {
2424
return isLegacyTemplate(template)
2525
? (template as LegacyTemplateSerialized)[setting]
26-
: (template as TemplateSerialized).template[setting];
26+
: (template as TemplateSerialized).template?.[setting];
2727
};

x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@ interface Props {
2121
value?: MappingsConfiguration;
2222
}
2323

24-
const stringifyJson = (json: GenericObject) =>
25-
Object.keys(json).length ? JSON.stringify(json, null, 2) : '{\n\n}';
26-
2724
const formSerializer: SerializerFunc<MappingsConfiguration> = (formData) => {
2825
const {
2926
dynamicMapping: {
@@ -40,22 +37,17 @@ const formSerializer: SerializerFunc<MappingsConfiguration> = (formData) => {
4037

4138
const dynamic = dynamicMappingsEnabled ? true : throwErrorsForUnmappedFields ? 'strict' : false;
4239

43-
let parsedMeta;
44-
try {
45-
parsedMeta = JSON.parse(metaField);
46-
} catch {
47-
parsedMeta = {};
48-
}
49-
50-
return {
40+
const serialized = {
5141
dynamic,
5242
numeric_detection,
5343
date_detection,
5444
dynamic_date_formats,
55-
_source: { ...sourceField },
56-
_meta: parsedMeta,
45+
_source: sourceField,
46+
_meta: metaField,
5747
_routing,
5848
};
49+
50+
return serialized;
5951
};
6052

6153
const formDeserializer = (formData: GenericObject) => {
@@ -64,7 +56,11 @@ const formDeserializer = (formData: GenericObject) => {
6456
numeric_detection,
6557
date_detection,
6658
dynamic_date_formats,
67-
_source: { enabled, includes, excludes },
59+
_source: { enabled, includes, excludes } = {} as {
60+
enabled?: boolean;
61+
includes?: string[];
62+
excludes?: string[];
63+
},
6864
_meta,
6965
_routing,
7066
} = formData;
@@ -82,7 +78,7 @@ const formDeserializer = (formData: GenericObject) => {
8278
includes,
8379
excludes,
8480
},
85-
metaField: stringifyJson(_meta),
81+
metaField: _meta ?? {},
8682
_routing,
8783
};
8884
};

x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,30 @@ export const configurationFormSchema: FormSchema<MappingsConfiguration> = {
4848
validator: isJsonField(
4949
i18n.translate('xpack.idxMgmt.mappingsEditor.configuration.metaFieldEditorJsonError', {
5050
defaultMessage: 'The _meta field JSON is not valid.',
51-
})
51+
}),
52+
{ allowEmptyString: true }
5253
),
5354
},
5455
],
56+
deserializer: (value: any) => {
57+
if (value === '') {
58+
return value;
59+
}
60+
return JSON.stringify(value, null, 2);
61+
},
62+
serializer: (value: string) => {
63+
try {
64+
const parsed = JSON.parse(value);
65+
// If an empty object was passed, strip out this value entirely.
66+
if (!Object.keys(parsed).length) {
67+
return undefined;
68+
}
69+
return parsed;
70+
} catch (error) {
71+
// swallow error and return non-parsed value;
72+
return value;
73+
}
74+
},
5575
},
5676
sourceField: {
5777
enabled: {

x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ interface Props {
2222
const stringifyJson = (json: { [key: string]: any }) =>
2323
Array.isArray(json) ? JSON.stringify(json, null, 2) : '[\n\n]';
2424

25-
const formSerializer: SerializerFunc<MappingsTemplates> = (formData) => {
25+
const formSerializer: SerializerFunc<MappingsTemplates | undefined> = (formData) => {
2626
const { dynamicTemplates } = formData;
2727

2828
let parsedTemplates;
@@ -34,12 +34,14 @@ const formSerializer: SerializerFunc<MappingsTemplates> = (formData) => {
3434
parsedTemplates = [parsedTemplates];
3535
}
3636
} catch {
37-
parsedTemplates = [];
37+
// Silently swallow errors
3838
}
3939

40-
return {
41-
dynamic_templates: parsedTemplates,
42-
};
40+
return Array.isArray(parsedTemplates) && parsedTemplates.length > 0
41+
? {
42+
dynamic_templates: parsedTemplates,
43+
}
44+
: undefined;
4345
};
4446

4547
const formDeserializer = (formData: { [key: string]: any }) => {

x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ export const getTypeMetaFromSource = (
199199
*
200200
* @param fieldsToNormalize The "properties" object from the mappings (or "fields" object for `text` and `keyword` types)
201201
*/
202-
export const normalize = (fieldsToNormalize: Fields): NormalizedFields => {
202+
export const normalize = (fieldsToNormalize: Fields = {}): NormalizedFields => {
203203
let maxNestedDepth = 0;
204204

205205
const normalizeFields = (

x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ export const MappingsEditor = React.memo(({ onChange, value, indexSettings }: Pr
3939
}
4040

4141
const {
42-
_source = {},
43-
_meta = {},
42+
_source,
43+
_meta,
4444
_routing,
4545
dynamic,
4646
numeric_detection,
4747
date_detection,
4848
dynamic_date_formats,
49-
properties = {},
49+
properties,
5050
dynamic_templates,
5151
} = mappingsDefinition;
5252

x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { normalize, deNormalize, stripUndefinedValues } from './lib';
1919

2020
type Mappings = MappingsTemplates &
2121
MappingsConfiguration & {
22-
properties: MappingsFields;
22+
properties?: MappingsFields;
2323
};
2424

2525
export interface Types {
@@ -31,7 +31,7 @@ export interface Types {
3131

3232
export interface OnUpdateHandlerArg {
3333
isValid?: boolean;
34-
getData: () => Mappings;
34+
getData: () => Mappings | undefined;
3535
validate: () => Promise<boolean>;
3636
}
3737

@@ -114,13 +114,18 @@ export const MappingsState = React.memo(({ children, onChange, value }: Props) =
114114
const configurationData = state.configuration.data.format();
115115
const templatesData = state.templates.data.format();
116116

117-
return {
117+
const output = {
118118
...stripUndefinedValues({
119119
...configurationData,
120120
...templatesData,
121121
}),
122-
properties: fields,
123122
};
123+
124+
if (fields && Object.keys(fields).length > 0) {
125+
output.properties = fields;
126+
}
127+
128+
return Object.keys(output).length > 0 ? (output as Mappings) : undefined;
124129
},
125130
validate: async () => {
126131
const configurationFormValidator =

x-pack/plugins/index_management/public/application/components/template_form/steps/step_components.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ export const StepComponents = ({ defaultValue = [], onChange, esDocsBase }: Prop
4545

4646
const onComponentSelectionChange = useCallback(
4747
(components: string[]) => {
48-
onChange({ isValid: true, validate: async () => true, getData: () => components });
48+
onChange({
49+
isValid: true,
50+
validate: async () => true,
51+
getData: () => (components.length > 0 ? components : undefined),
52+
});
4953
},
5054
[onChange]
5155
);

x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ function formDeserializer(formData: LogisticsForm): LogisticsFormInternal {
103103
}
104104

105105
function formSerializer(formData: LogisticsFormInternal): LogisticsForm {
106-
const { __internal__, ...data } = formData;
107-
return data;
106+
const { __internal__, ...rest } = formData;
107+
return rest;
108108
}
109109

110110
export const StepLogistics: React.FunctionComponent<Props> = React.memo(

x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,7 @@ export const TemplateForm = ({
9191
const indexTemplate = defaultValue ?? {
9292
name: '',
9393
indexPatterns: [],
94-
composedOf: [],
95-
template: {
96-
settings: {},
97-
mappings: {},
98-
aliases: {},
99-
},
94+
template: {},
10095
_kbnMeta: {
10196
isManaged: false,
10297
isCloudManaged: false,
@@ -150,18 +145,50 @@ export const TemplateForm = ({
150145
</>
151146
) : null;
152147

153-
const buildTemplateObject = (initialTemplate: TemplateDeserialized) => (
154-
wizardData: WizardContent
155-
): TemplateDeserialized => ({
156-
...initialTemplate,
157-
...wizardData.logistics,
158-
composedOf: wizardData.components,
159-
template: {
160-
settings: wizardData.settings,
161-
mappings: wizardData.mappings,
162-
aliases: wizardData.aliases,
148+
/**
149+
* If no mappings, settings or aliases are defined, it is better to not send empty
150+
* object for those values.
151+
* This method takes care of that and other cleanup of empty fields.
152+
* @param template The template object to clean up
153+
*/
154+
const cleanupTemplateObject = (template: TemplateDeserialized) => {
155+
const outputTemplate = { ...template };
156+
157+
if (outputTemplate.template.settings === undefined) {
158+
delete outputTemplate.template.settings;
159+
}
160+
if (outputTemplate.template.mappings === undefined) {
161+
delete outputTemplate.template.mappings;
162+
}
163+
if (outputTemplate.template.aliases === undefined) {
164+
delete outputTemplate.template.aliases;
165+
}
166+
if (Object.keys(outputTemplate.template).length === 0) {
167+
delete outputTemplate.template;
168+
}
169+
170+
return outputTemplate;
171+
};
172+
173+
const buildTemplateObject = useCallback(
174+
(initialTemplate: TemplateDeserialized) => (
175+
wizardData: WizardContent
176+
): TemplateDeserialized => {
177+
const outputTemplate = {
178+
...initialTemplate,
179+
...wizardData.logistics,
180+
composedOf: wizardData.components,
181+
template: {
182+
settings: wizardData.settings,
183+
mappings: wizardData.mappings,
184+
aliases: wizardData.aliases,
185+
},
186+
};
187+
188+
return cleanupTemplateObject(outputTemplate);
163189
},
164-
});
190+
[]
191+
);
165192

166193
const onSaveTemplate = useCallback(
167194
async (wizardData: WizardContent) => {
@@ -177,7 +204,7 @@ export const TemplateForm = ({
177204

178205
clearSaveError();
179206
},
180-
[indexTemplate, onSave, clearSaveError]
207+
[indexTemplate, buildTemplateObject, onSave, clearSaveError]
181208
);
182209

183210
return (

0 commit comments

Comments
 (0)