Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Mappings Editor] Add support for synthetic _source #199854

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
96ec5ac
[Mappings Editor] Add support for synthetic _source
ElenaStoeva Nov 12, 2024
de0be81
Fix deserialization
ElenaStoeva Nov 14, 2024
853f9ff
Fix error in component template form mappings editor
ElenaStoeva Nov 14, 2024
92adc29
Fix existing jest tests
ElenaStoeva Nov 14, 2024
32d6672
Add jest tests
ElenaStoeva Nov 14, 2024
9fedad6
Merge branch 'main' into mappings-configuration/source-field
ElenaStoeva Nov 14, 2024
16566e4
Fix copy
ElenaStoeva Nov 14, 2024
991cb4f
[CI] Auto-commit changed files from 'make api-docs'
kibanamachine Nov 14, 2024
b485c9a
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 14, 2024
a131400
Fix _source field value change when index mode is logsdb
ElenaStoeva Nov 18, 2024
fc5c998
Merge branch 'mappings-configuration/source-field' of https://github.…
ElenaStoeva Nov 18, 2024
aa2b19b
Fix typo in copy
ElenaStoeva Nov 18, 2024
772ede9
Fix i18n errors
ElenaStoeva Nov 18, 2024
a8ae5d6
Fix jest tests
ElenaStoeva Nov 18, 2024
fbe99d4
Merge branch 'main' into mappings-configuration/source-field
ElenaStoeva Nov 18, 2024
6c7e5c5
Refactor changes
ElenaStoeva Nov 19, 2024
57624e1
Merge branch 'mappings-configuration/source-field' of https://github.…
ElenaStoeva Nov 19, 2024
0c70fce
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 19, 2024
1c8422a
Fix type errors
ElenaStoeva Nov 19, 2024
117ec85
Fix merge conflicts
ElenaStoeva Nov 19, 2024
1907323
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 19, 2024
4b89cac
Fix jest tests
ElenaStoeva Nov 19, 2024
12d3b27
Add serialization tests
ElenaStoeva Nov 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/kbn-doc-links/src/get_doc_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D
mappingSimilarity: `${ELASTICSEARCH_DOCS}similarity.html`,
mappingSourceFields: `${ELASTICSEARCH_DOCS}mapping-source-field.html`,
mappingSourceFieldsDisable: `${ELASTICSEARCH_DOCS}mapping-source-field.html#disable-source-field`,
mappingSyntheticSourceFields: `${ELASTICSEARCH_DOCS}mapping-source-field.html#synthetic-source`,
mappingStore: `${ELASTICSEARCH_DOCS}mapping-store.html`,
mappingSubobjects: `${ELASTICSEARCH_DOCS}subobjects.html`,
mappingTermVector: `${ELASTICSEARCH_DOCS}term-vector.html`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ export type TestSubjects =
| 'advancedConfiguration.dynamicMappingsToggle.input'
| 'advancedConfiguration.metaField'
| 'advancedConfiguration.routingRequiredToggle.input'
| 'sourceValueField'
| 'sourceField.includesField'
| 'sourceField.excludesField'
| 'dynamicTemplatesEditor'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,24 @@ describe('Mappings editor: core', () => {
let onChangeHandler: jest.Mock = jest.fn();
let getMappingsEditorData = getMappingsEditorDataFactory(onChangeHandler);
let testBed: MappingsEditorTestBed;
const appDependencies = { plugins: { ml: { mlApi: {} } } };
let hasEnterpriseLicense = true;
const mockLicenseCheck = jest.fn((type: any) => hasEnterpriseLicense);
const appDependencies = {
plugins: {
ml: { mlApi: {} },
licensing: {
license$: {
subscribe: jest.fn((callback: any) => {
callback({
isActive: true,
hasAtLeast: mockLicenseCheck,
});
return { unsubscribe: jest.fn() };
}),
},
},
},
};

beforeAll(() => {
jest.useFakeTimers({ legacyFakeTimers: true });
Expand Down Expand Up @@ -456,13 +473,96 @@ describe('Mappings editor: core', () => {
updatedMappings = {
...updatedMappings,
dynamic: false,
// The "enabled": true is removed as this is the default in Es
_source: {
includes: defaultMappings._source.includes,
excludes: defaultMappings._source.excludes,
},
};
delete updatedMappings.date_detection;
delete updatedMappings.dynamic_date_formats;
delete updatedMappings.numeric_detection;

expect(data).toEqual(updatedMappings);
});

describe('props.indexMode sets the correct default value of _source field', () => {
it("defaults to 'stored' with 'standard' index mode prop", async () => {
await act(async () => {
testBed = setup(
{
value: { ...defaultMappings, _source: undefined },
onChange: onChangeHandler,
indexMode: 'standard',
},
ctx
);
});
testBed.component.update();

const {
actions: { selectTab },
find,
} = testBed;

await selectTab('advanced');

// Check that the stored option is selected
expect(find('sourceValueField').prop('value')).toBe('stored');
});

['logsdb', 'time_series'].forEach((indexMode) => {
it(`defaults to 'synthetic' with ${indexMode} index mode prop on enterprise license`, async () => {
hasEnterpriseLicense = true;
await act(async () => {
testBed = setup(
{
value: { ...defaultMappings, _source: undefined },
onChange: onChangeHandler,
indexMode,
},
ctx
);
});
testBed.component.update();

const {
actions: { selectTab },
find,
} = testBed;

await selectTab('advanced');

// Check that the synthetic option is selected
expect(find('sourceValueField').prop('value')).toBe('synthetic');
});

it(`defaults to 'standard' with ${indexMode} index mode prop on basic license`, async () => {
hasEnterpriseLicense = false;
await act(async () => {
testBed = setup(
{
value: { ...defaultMappings, _source: undefined },
onChange: onChangeHandler,
indexMode,
},
ctx
);
});
testBed.component.update();

const {
actions: { selectTab },
find,
} = testBed;

await selectTab('advanced');

// Check that the stored option is selected
expect(find('sourceValueField').prop('value')).toBe('stored');
});
});
});
});

describe('multi-fields support', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@
* 2.0.
*/

import React, { useEffect, useRef, useCallback } from 'react';
import React, { useEffect, useRef } from 'react';
import { EuiSpacer } from '@elastic/eui';

import { FormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import { useAppContext } from '../../../../app_context';
import { useForm, Form } from '../../shared_imports';
import { GenericObject, MappingsConfiguration } from '../../types';
import { MapperSizePluginId } from '../../constants';
import { useDispatch } from '../../mappings_state_context';
import { DynamicMappingSection } from './dynamic_mapping_section';
import { SourceFieldSection } from './source_field_section';
import {
SourceFieldSection,
STORED_SOURCE_OPTION,
SYNTHETIC_SOURCE_OPTION,
DISABLED_SOURCE_OPTION,
} from './source_field_section';
import { MetaFieldSection } from './meta_field_section';
import { RoutingSection } from './routing_section';
import { MapperSizePluginSection } from './mapper_size_plugin_section';
Expand All @@ -28,7 +32,14 @@ interface Props {
esNodesPlugins: string[];
}

const formSerializer = (formData: GenericObject, sourceFieldMode?: string) => {
interface SerializedSourceField {
enabled?: boolean;
mode?: string;
includes?: string[];
excludes?: string[];
}

export const formSerializer = (formData: GenericObject) => {
const { dynamicMapping, sourceField, metaField, _routing, _size, subobjects } = formData;

const dynamic = dynamicMapping?.enabled
Expand All @@ -37,12 +48,30 @@ const formSerializer = (formData: GenericObject, sourceFieldMode?: string) => {
? 'strict'
: dynamicMapping?.enabled;

const _source =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: It might just be me, but this seems a little too complex to read at first glance. Perhaps a more explicit structure could make future extensions more straightforward? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally understand! I did my best to make it as clear as possible but it's a bit tricky given that the sourceField.option field actually controls two properties in Es (mode and enabled). We also don't want to add the mode property if the field is undefined (which means it hasn't been set by the user) - otherwise, just opening the "Advanced options" tab would insert a "mode" property which is a behavior that we recently managed to fix in this form. I'm open to any suggestions for shortening/simplifying the code.

sourceField?.option === SYNTHETIC_SOURCE_OPTION
? { mode: SYNTHETIC_SOURCE_OPTION }
: sourceField?.option === DISABLED_SOURCE_OPTION
? { enabled: false }
: sourceField?.option === STORED_SOURCE_OPTION
? {
mode: 'stored',
includes: sourceField?.includes,
excludes: sourceField?.excludes,
}
: sourceField?.includes || sourceField?.excludes
? {
includes: sourceField?.includes,
excludes: sourceField?.excludes,
}
: undefined;

const serialized = {
dynamic,
numeric_detection: dynamicMapping?.numeric_detection,
date_detection: dynamicMapping?.date_detection,
dynamic_date_formats: dynamicMapping?.dynamic_date_formats,
_source: sourceFieldMode ? { mode: sourceFieldMode } : sourceField,
_source: _source as SerializedSourceField,
_meta: metaField,
_routing,
_size,
Expand All @@ -52,19 +81,15 @@ const formSerializer = (formData: GenericObject, sourceFieldMode?: string) => {
return serialized;
};

const formDeserializer = (formData: GenericObject) => {
export const formDeserializer = (formData: GenericObject) => {
const {
dynamic,
/* eslint-disable @typescript-eslint/naming-convention */
numeric_detection,
date_detection,
dynamic_date_formats,
/* eslint-enable @typescript-eslint/naming-convention */
_source: { enabled, includes, excludes } = {} as {
enabled?: boolean;
includes?: string[];
excludes?: string[];
},
_source: { enabled, mode, includes, excludes } = {} as SerializedSourceField,
_meta,
_routing,
// For the Mapper Size plugin
Expand All @@ -81,7 +106,14 @@ const formDeserializer = (formData: GenericObject) => {
dynamic_date_formats,
},
sourceField: {
enabled,
option:
mode === 'stored'
? STORED_SOURCE_OPTION
: mode === 'synthetic'
? SYNTHETIC_SOURCE_OPTION
: enabled === false
? DISABLED_SOURCE_OPTION
: undefined,
includes,
excludes,
},
Expand All @@ -99,14 +131,9 @@ export const ConfigurationForm = React.memo(({ value, esNodesPlugins }: Props) =

const isMounted = useRef(false);

const serializerCallback = useCallback(
(formData: FormData) => formSerializer(formData, value?._source?.mode),
[value?._source?.mode]
);

const { form } = useForm({
schema: configurationFormSchema,
serializer: serializerCallback,
serializer: formSerializer,
deserializer: formDeserializer,
defaultValue: value,
id: 'configurationForm',
Expand Down Expand Up @@ -165,7 +192,7 @@ export const ConfigurationForm = React.memo(({ value, esNodesPlugins }: Props) =
<EuiSpacer size="xl" />
<MetaFieldSection />
<EuiSpacer size="xl" />
{enableMappingsSourceFieldSection && !value?._source?.mode && (
{enableMappingsSourceFieldSection && (
<>
<SourceFieldSection /> <EuiSpacer size="xl" />
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,9 @@ export const configurationFormSchema: FormSchema = {
},
},
sourceField: {
enabled: {
label: i18n.translate('xpack.idxMgmt.mappingsEditor.configuration.sourceFieldLabel', {
defaultMessage: 'Enable _source field',
}),
type: FIELD_TYPES.TOGGLE,
defaultValue: true,
option: {
type: FIELD_TYPES.SUPER_SELECT,
defaultValue: 'stored',
},
includes: {
label: i18n.translate('xpack.idxMgmt.mappingsEditor.configuration.includeSourceFieldsLabel', {
Expand Down
Loading