Skip to content

Commit 82478c3

Browse files
cjcenizalkibanamachine
andauthoredOct 12, 2020
Revert "Revert "Add support for runtime field types to mappings editor. (#77420)" (#79611)" (#79940)
This reverts commit e01d538. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
1 parent a89a4b1 commit 82478c3

27 files changed

+552
-74
lines changed
 

‎x-pack/plugins/index_management/public/application/app_context.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import React, { createContext, useContext } from 'react';
88
import { ScopedHistory } from 'kibana/public';
99
import { ManagementAppMountParams } from 'src/plugins/management/public';
1010
import { UsageCollectionSetup } from 'src/plugins/usage_collection/public';
11-
import { CoreStart } from '../../../../../src/core/public';
11+
import { CoreSetup, CoreStart } from '../../../../../src/core/public';
1212

1313
import { IngestManagerSetup } from '../../../ingest_manager/public';
1414
import { IndexMgmtMetricsType } from '../types';
@@ -34,6 +34,7 @@ export interface AppDependencies {
3434
};
3535
history: ScopedHistory;
3636
setBreadcrumbs: ManagementAppMountParams['setBreadcrumbs'];
37+
uiSettings: CoreSetup['uiSettings'];
3738
}
3839

3940
export const AppContextProvider = ({

‎x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ export * from './meta_parameter';
7373

7474
export * from './ignore_above_parameter';
7575

76+
export { RuntimeTypeParameter } from './runtime_type_parameter';
77+
78+
export { PainlessScriptParameter } from './painless_script_parameter';
79+
7680
export const PARAMETER_SERIALIZERS = [relationsSerializer, dynamicSerializer];
7781

7882
export const PARAMETER_DESERIALIZERS = [relationsDeserializer, dynamicDeserializer];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import React from 'react';
8+
import { i18n } from '@kbn/i18n';
9+
import { EuiFormRow, EuiDescribedFormGroup } from '@elastic/eui';
10+
11+
import { CodeEditor, UseField } from '../../../shared_imports';
12+
import { getFieldConfig } from '../../../lib';
13+
import { EditFieldFormRow } from '../fields/edit_field';
14+
15+
interface Props {
16+
stack?: boolean;
17+
}
18+
19+
export const PainlessScriptParameter = ({ stack }: Props) => {
20+
return (
21+
<UseField path="script.source" config={getFieldConfig('script')}>
22+
{(scriptField) => {
23+
const error = scriptField.getErrorsMessages();
24+
const isInvalid = error ? Boolean(error.length) : false;
25+
26+
const field = (
27+
<EuiFormRow label={scriptField.label} error={error} isInvalid={isInvalid} fullWidth>
28+
<CodeEditor
29+
languageId="painless"
30+
// 99% width allows the editor to resize horizontally. 100% prevents it from resizing.
31+
width="99%"
32+
height="400px"
33+
value={scriptField.value as string}
34+
onChange={scriptField.setValue}
35+
options={{
36+
fontSize: 12,
37+
minimap: {
38+
enabled: false,
39+
},
40+
scrollBeyondLastLine: false,
41+
wordWrap: 'on',
42+
wrappingIndent: 'indent',
43+
automaticLayout: true,
44+
}}
45+
/>
46+
</EuiFormRow>
47+
);
48+
49+
const fieldTitle = i18n.translate('xpack.idxMgmt.mappingsEditor.painlessScript.title', {
50+
defaultMessage: 'Emitted value',
51+
});
52+
53+
const fieldDescription = i18n.translate(
54+
'xpack.idxMgmt.mappingsEditor.painlessScript.description',
55+
{
56+
defaultMessage: 'Use emit() to define the value of this runtime field.',
57+
}
58+
);
59+
60+
if (stack) {
61+
return (
62+
<EditFieldFormRow title={fieldTitle} description={fieldDescription} withToggle={false}>
63+
{field}
64+
</EditFieldFormRow>
65+
);
66+
}
67+
68+
return (
69+
<EuiDescribedFormGroup
70+
title={<h3>{fieldTitle}</h3>}
71+
description={fieldDescription}
72+
fullWidth={true}
73+
>
74+
{field}
75+
</EuiDescribedFormGroup>
76+
);
77+
}}
78+
</UseField>
79+
);
80+
};

‎x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx

+11-11
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,17 @@ export const PathParameter = ({ field, allFields }: Props) => {
9393
<>
9494
{!Boolean(suggestedFields.length) && (
9595
<>
96-
<EuiCallOut color="warning">
97-
<p>
98-
{i18n.translate(
99-
'xpack.idxMgmt.mappingsEditor.aliasType.noFieldsAddedWarningMessage',
100-
{
101-
defaultMessage:
102-
'You need to add at least one field before creating an alias.',
103-
}
104-
)}
105-
</p>
106-
</EuiCallOut>
96+
<EuiCallOut
97+
size="s"
98+
color="warning"
99+
title={i18n.translate(
100+
'xpack.idxMgmt.mappingsEditor.aliasType.noFieldsAddedWarningMessage',
101+
{
102+
defaultMessage:
103+
'You need to add at least one field before creating an alias.',
104+
}
105+
)}
106+
/>
107107
<EuiSpacer />
108108
</>
109109
)}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import React from 'react';
8+
import { i18n } from '@kbn/i18n';
9+
import {
10+
EuiFormRow,
11+
EuiComboBox,
12+
EuiComboBoxOptionOption,
13+
EuiDescribedFormGroup,
14+
EuiSpacer,
15+
} from '@elastic/eui';
16+
17+
import { UseField } from '../../../shared_imports';
18+
import { DataType } from '../../../types';
19+
import { getFieldConfig } from '../../../lib';
20+
import { RUNTIME_FIELD_OPTIONS, TYPE_DEFINITION } from '../../../constants';
21+
import { EditFieldFormRow, FieldDescriptionSection } from '../fields/edit_field';
22+
23+
interface Props {
24+
stack?: boolean;
25+
}
26+
27+
export const RuntimeTypeParameter = ({ stack }: Props) => {
28+
return (
29+
<UseField path="runtime_type" config={getFieldConfig('runtime_type')}>
30+
{(runtimeTypeField) => {
31+
const { label, value, setValue } = runtimeTypeField;
32+
const typeDefinition =
33+
TYPE_DEFINITION[(value as EuiComboBoxOptionOption[])[0]!.value as DataType];
34+
35+
const field = (
36+
<>
37+
<EuiFormRow label={label} fullWidth>
38+
<EuiComboBox
39+
placeholder={i18n.translate(
40+
'xpack.idxMgmt.mappingsEditor.runtimeType.placeholderLabel',
41+
{
42+
defaultMessage: 'Select a type',
43+
}
44+
)}
45+
singleSelection={{ asPlainText: true }}
46+
options={RUNTIME_FIELD_OPTIONS}
47+
selectedOptions={value as EuiComboBoxOptionOption[]}
48+
onChange={setValue}
49+
isClearable={false}
50+
fullWidth
51+
/>
52+
</EuiFormRow>
53+
54+
<EuiSpacer size="m" />
55+
56+
{/* Field description */}
57+
{typeDefinition && (
58+
<FieldDescriptionSection isMultiField={false}>
59+
{typeDefinition.description?.() as JSX.Element}
60+
</FieldDescriptionSection>
61+
)}
62+
</>
63+
);
64+
65+
const fieldTitle = i18n.translate('xpack.idxMgmt.mappingsEditor.runtimeType.title', {
66+
defaultMessage: 'Emitted type',
67+
});
68+
69+
const fieldDescription = i18n.translate(
70+
'xpack.idxMgmt.mappingsEditor.runtimeType.description',
71+
{
72+
defaultMessage: 'Select the type of value emitted by the runtime field.',
73+
}
74+
);
75+
76+
if (stack) {
77+
return (
78+
<EditFieldFormRow title={fieldTitle} description={fieldDescription} withToggle={false}>
79+
{field}
80+
</EditFieldFormRow>
81+
);
82+
}
83+
84+
return (
85+
<EuiDescribedFormGroup
86+
title={<h3>{fieldTitle}</h3>}
87+
description={fieldDescription}
88+
fullWidth={true}
89+
>
90+
{field}
91+
</EuiDescribedFormGroup>
92+
);
93+
}}
94+
</UseField>
95+
);
96+
};

‎x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx

+9-6
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,17 @@ export const TermVectorParameter = ({ field, defaultToggleValue }: Props) => {
5656
{formData.term_vector === 'with_positions_offsets' && (
5757
<>
5858
<EuiSpacer size="s" />
59-
<EuiCallOut color="warning">
60-
<p>
61-
{i18n.translate('xpack.idxMgmt.mappingsEditor.termVectorFieldWarningMessage', {
59+
<EuiCallOut
60+
size="s"
61+
color="warning"
62+
title={i18n.translate(
63+
'xpack.idxMgmt.mappingsEditor.termVectorFieldWarningMessage',
64+
{
6265
defaultMessage:
6366
'Setting "With positions and offsets" will double the size of a field’s index.',
64-
})}
65-
</p>
66-
</EuiCallOut>
67+
}
68+
)}
69+
/>
6770
</>
6871
)}
6972
</>

‎x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx

+17-6
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,17 @@ import {
1414
EuiFlexGroup,
1515
EuiFlexItem,
1616
EuiOutsideClickDetector,
17+
EuiSpacer,
1718
} from '@elastic/eui';
1819

1920
import { useForm, Form, FormDataProvider } from '../../../../shared_imports';
20-
import { EUI_SIZE } from '../../../../constants';
21+
import { EUI_SIZE, TYPE_DEFINITION } from '../../../../constants';
2122
import { useDispatch } from '../../../../mappings_state_context';
2223
import { fieldSerializer } from '../../../../lib';
23-
import { Field, NormalizedFields } from '../../../../types';
24+
import { Field, NormalizedFields, MainType } from '../../../../types';
2425
import { NameParameter, TypeParameter, SubTypeParameter } from '../../field_parameters';
25-
import { getParametersFormForType } from './required_parameters_forms';
26+
import { FieldBetaBadge } from '../field_beta_badge';
27+
import { getRequiredParametersFormForType } from './required_parameters_forms';
2628

2729
const formWrapper = (props: any) => <form {...props} />;
2830

@@ -195,18 +197,27 @@ export const CreateField = React.memo(function CreateFieldComponent({
195197

196198
<FormDataProvider pathsToWatch={['type', 'subType']}>
197199
{({ type, subType }) => {
198-
const ParametersForm = getParametersFormForType(
200+
const RequiredParametersForm = getRequiredParametersFormForType(
199201
type?.[0].value,
200202
subType?.[0].value
201203
);
202204

203-
if (!ParametersForm) {
205+
if (!RequiredParametersForm) {
204206
return null;
205207
}
206208

209+
const typeDefinition = TYPE_DEFINITION[type?.[0].value as MainType];
210+
207211
return (
208212
<div className="mappingsEditor__createFieldRequiredProps">
209-
<ParametersForm key={subType ?? type} allFields={allFields} />
213+
{typeDefinition.isBeta ? (
214+
<>
215+
<FieldBetaBadge />
216+
<EuiSpacer size="m" />
217+
</>
218+
) : null}
219+
220+
<RequiredParametersForm key={subType ?? type} allFields={allFields} />
210221
</div>
211222
);
212223
}}

‎x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { AliasTypeRequiredParameters } from './alias_type';
1111
import { TokenCountTypeRequiredParameters } from './token_count_type';
1212
import { ScaledFloatTypeRequiredParameters } from './scaled_float_type';
1313
import { DenseVectorRequiredParameters } from './dense_vector_type';
14+
import { RuntimeTypeRequiredParameters } from './runtime_type';
1415

1516
export interface ComponentProps {
1617
allFields: NormalizedFields['byId'];
@@ -21,9 +22,10 @@ const typeToParametersFormMap: { [key in DataType]?: ComponentType<any> } = {
2122
token_count: TokenCountTypeRequiredParameters,
2223
scaled_float: ScaledFloatTypeRequiredParameters,
2324
dense_vector: DenseVectorRequiredParameters,
25+
runtime: RuntimeTypeRequiredParameters,
2426
};
2527

26-
export const getParametersFormForType = (
28+
export const getRequiredParametersFormForType = (
2729
type: MainType,
2830
subType?: SubType
2931
): ComponentType<ComponentProps> | undefined =>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import React from 'react';
8+
9+
import { RuntimeTypeParameter, PainlessScriptParameter } from '../../../field_parameters';
10+
11+
export const RuntimeTypeRequiredParameters = () => {
12+
return (
13+
<>
14+
<RuntimeTypeParameter />
15+
<PainlessScriptParameter />
16+
</>
17+
);
18+
};

‎x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx

+44-22
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
*/
66

77
import React from 'react';
8-
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
8+
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
99

1010
import { FormDataProvider } from '../../../../shared_imports';
11-
import { MainType, SubType, Field } from '../../../../types';
11+
import { MainType, SubType, Field, DataTypeDefinition } from '../../../../types';
1212
import { TYPE_DEFINITION } from '../../../../constants';
1313
import { NameParameter, TypeParameter, SubTypeParameter } from '../../field_parameters';
14+
import { FieldBetaBadge } from '../field_beta_badge';
1415
import { FieldDescriptionSection } from './field_description_section';
1516

1617
interface Props {
@@ -19,6 +20,25 @@ interface Props {
1920
isMultiField: boolean;
2021
}
2122

23+
const getTypeDefinition = (type: MainType, subType: SubType): DataTypeDefinition | undefined => {
24+
if (!type) {
25+
return;
26+
}
27+
28+
const typeDefinition = TYPE_DEFINITION[type];
29+
const hasSubType = typeDefinition.subTypes !== undefined;
30+
31+
if (hasSubType) {
32+
if (subType) {
33+
return TYPE_DEFINITION[subType];
34+
}
35+
36+
return;
37+
}
38+
39+
return typeDefinition;
40+
};
41+
2242
export const EditFieldHeaderForm = React.memo(
2343
({ defaultValue, isRootLevelField, isMultiField }: Props) => {
2444
return (
@@ -56,27 +76,29 @@ export const EditFieldHeaderForm = React.memo(
5676
</EuiFlexGroup>
5777

5878
{/* Field description */}
59-
<FieldDescriptionSection isMultiField={isMultiField}>
60-
<FormDataProvider pathsToWatch={['type', 'subType']}>
61-
{({ type, subType }) => {
62-
if (!type) {
63-
return null;
64-
}
65-
const typeDefinition = TYPE_DEFINITION[type[0].value as MainType];
66-
const hasSubType = typeDefinition.subTypes !== undefined;
67-
68-
if (hasSubType) {
69-
if (subType) {
70-
const subTypeDefinition = TYPE_DEFINITION[subType as SubType];
71-
return (subTypeDefinition?.description?.() as JSX.Element) ?? null;
72-
}
73-
return null;
74-
}
79+
<FormDataProvider pathsToWatch={['type', 'subType']}>
80+
{({ type, subType }) => {
81+
const typeDefinition = getTypeDefinition(
82+
type[0].value as MainType,
83+
subType && (subType[0].value as SubType)
84+
);
85+
const description = (typeDefinition?.description?.() as JSX.Element) ?? null;
7586

76-
return typeDefinition.description?.() as JSX.Element;
77-
}}
78-
</FormDataProvider>
79-
</FieldDescriptionSection>
87+
return (
88+
<>
89+
<EuiSpacer size="l" />
90+
91+
{typeDefinition?.isBeta && <FieldBetaBadge />}
92+
93+
<EuiSpacer size="s" />
94+
95+
<FieldDescriptionSection isMultiField={isMultiField}>
96+
{description}
97+
</FieldDescriptionSection>
98+
</>
99+
);
100+
}}
101+
</FormDataProvider>
80102
</>
81103
);
82104
}

‎x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66
import React from 'react';
7-
import { EuiSpacer, EuiText } from '@elastic/eui';
7+
import { EuiText } from '@elastic/eui';
88
import { i18n } from '@kbn/i18n';
99

1010
interface Props {
@@ -19,7 +19,6 @@ export const FieldDescriptionSection = ({ children, isMultiField }: Props) => {
1919

2020
return (
2121
<section>
22-
<EuiSpacer size="l" />
2322
<EuiText size="s" color="subdued">
2423
{children}
2524

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import React from 'react';
8+
import { EuiBetaBadge } from '@elastic/eui';
9+
import { i18n } from '@kbn/i18n';
10+
11+
export const FieldBetaBadge = () => {
12+
const betaText = i18n.translate('xpack.idxMgmt.mappingsEditor.fieldBetaBadgeLabel', {
13+
defaultMessage: 'Beta',
14+
});
15+
16+
const tooltipText = i18n.translate('xpack.idxMgmt.mappingsEditor.fieldBetaBadgeTooltip', {
17+
defaultMessage: 'This field type is not GA. Please help us by reporting any bugs.',
18+
});
19+
20+
return <EuiBetaBadge label={betaText} tooltipContent={tooltipText} />;
21+
};

‎x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { JoinType } from './join_type';
3131
import { HistogramType } from './histogram_type';
3232
import { ConstantKeywordType } from './constant_keyword_type';
3333
import { RankFeatureType } from './rank_feature_type';
34+
import { RuntimeType } from './runtime_type';
3435
import { WildcardType } from './wildcard_type';
3536
import { PointType } from './point_type';
3637
import { VersionType } from './version_type';
@@ -61,6 +62,7 @@ const typeToParametersFormMap: { [key in DataType]?: ComponentType<any> } = {
6162
histogram: HistogramType,
6263
constant_keyword: ConstantKeywordType,
6364
rank_feature: RankFeatureType,
65+
runtime: RuntimeType,
6466
wildcard: WildcardType,
6567
point: PointType,
6668
version: VersionType,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import React from 'react';
8+
9+
import { RuntimeTypeParameter, PainlessScriptParameter } from '../../field_parameters';
10+
import { BasicParametersSection } from '../edit_field';
11+
12+
export const RuntimeType = () => {
13+
return (
14+
<BasicParametersSection>
15+
<RuntimeTypeParameter stack={true} />
16+
<PainlessScriptParameter stack={true} />
17+
</BasicParametersSection>
18+
);
19+
};

‎x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
import { i18n } from '@kbn/i18n';
1717

1818
import { NormalizedField, NormalizedFields } from '../../../types';
19-
import { getTypeLabelFromType } from '../../../lib';
19+
import { getTypeLabelFromField } from '../../../lib';
2020
import { CHILD_FIELD_INDENT_SIZE, LEFT_PADDING_SIZE_FIELD_ITEM_WRAPPER } from '../../../constants';
2121

2222
import { FieldsList } from './fields_list';
@@ -67,6 +67,7 @@ function FieldListItemComponent(
6767
isExpanded,
6868
path,
6969
} = field;
70+
7071
// When there aren't any "child" fields (the maxNestedDepth === 0), there is no toggle icon on the left of any field.
7172
// For that reason, we need to compensate and substract some indent to left align on the page.
7273
const substractIndentAmount = maxNestedDepth === 0 ? CHILD_FIELD_INDENT_SIZE * 0.5 : 0;
@@ -280,10 +281,10 @@ function FieldListItemComponent(
280281
? i18n.translate('xpack.idxMgmt.mappingsEditor.multiFieldBadgeLabel', {
281282
defaultMessage: '{dataType} multi-field',
282283
values: {
283-
dataType: getTypeLabelFromType(source.type),
284+
dataType: getTypeLabelFromField(source),
284285
},
285286
})
286-
: getTypeLabelFromType(source.type)}
287+
: getTypeLabelFromField(source)}
287288
</EuiBadge>
288289
</EuiFlexItem>
289290

‎x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n';
1111
import { SearchResult } from '../../../types';
1212
import { TYPE_DEFINITION } from '../../../constants';
1313
import { useDispatch } from '../../../mappings_state_context';
14-
import { getTypeLabelFromType } from '../../../lib';
14+
import { getTypeLabelFromField } from '../../../lib';
1515
import { DeleteFieldProvider } from '../fields/delete_field_provider';
1616

1717
interface Props {
@@ -121,7 +121,7 @@ export const SearchResultItem = React.memo(function FieldListItemFlatComponent({
121121
dataType: TYPE_DEFINITION[source.type].label,
122122
},
123123
})
124-
: getTypeLabelFromType(source.type)}
124+
: getTypeLabelFromField(source)}
125125
</EuiBadge>
126126
</EuiFlexItem>
127127

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

+20
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,25 @@ import { documentationService } from '../../../services/documentation';
1313
import { MainType, SubType, DataType, DataTypeDefinition } from '../types';
1414

1515
export const TYPE_DEFINITION: { [key in DataType]: DataTypeDefinition } = {
16+
runtime: {
17+
value: 'runtime',
18+
isBeta: true,
19+
label: i18n.translate('xpack.idxMgmt.mappingsEditor.dataType.runtimeFieldDescription', {
20+
defaultMessage: 'Runtime',
21+
}),
22+
// TODO: Add this once the page exists.
23+
// documentation: {
24+
// main: '/runtime_field.html',
25+
// },
26+
description: () => (
27+
<p>
28+
<FormattedMessage
29+
id="xpack.idxMgmt.mappingsEditor.dataType.runtimeFieldLongDescription"
30+
defaultMessage="Runtime fields define scripts that calculate field values at runtime."
31+
/>
32+
</p>
33+
),
34+
},
1635
text: {
1736
value: 'text',
1837
label: i18n.translate('xpack.idxMgmt.mappingsEditor.dataType.textDescription', {
@@ -925,6 +944,7 @@ export const MAIN_TYPES: MainType[] = [
925944
'range',
926945
'rank_feature',
927946
'rank_features',
947+
'runtime',
928948
'search_as_you_type',
929949
'shape',
930950
'text',

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

+30
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const TYPE_NOT_ALLOWED_MULTIFIELD: DataType[] = [
1818
'object',
1919
'nested',
2020
'alias',
21+
'runtime',
2122
];
2223

2324
export const FIELD_TYPES_OPTIONS = Object.entries(MAIN_DATA_TYPE_DEFINITION).map(
@@ -27,6 +28,35 @@ export const FIELD_TYPES_OPTIONS = Object.entries(MAIN_DATA_TYPE_DEFINITION).map
2728
})
2829
) as ComboBoxOption[];
2930

31+
export const RUNTIME_FIELD_OPTIONS = [
32+
{
33+
label: 'Keyword',
34+
value: 'keyword',
35+
},
36+
{
37+
label: 'Long',
38+
value: 'long',
39+
},
40+
{
41+
label: 'Double',
42+
value: 'double',
43+
},
44+
{
45+
label: 'Date',
46+
value: 'date',
47+
},
48+
{
49+
label: 'IP',
50+
value: 'ip',
51+
},
52+
{
53+
label: 'Boolean',
54+
value: 'boolean',
55+
},
56+
] as ComboBoxOption[];
57+
58+
export const RUNTIME_FIELD_TYPES = ['keyword', 'long', 'double', 'date', 'ip', 'boolean'] as const;
59+
3060
interface SuperSelectOptionConfig {
3161
inputDisplay: string;
3262
dropdownDisplay: JSX.Element;

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

+48
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ import {
2020
import {
2121
AliasOption,
2222
DataType,
23+
RuntimeType,
2324
ComboBoxOption,
2425
ParameterName,
2526
ParameterDefinition,
2627
} from '../types';
2728
import { documentationService } from '../../../services/documentation';
2829
import { INDEX_DEFAULT } from './default_values';
2930
import { TYPE_DEFINITION } from './data_types_definition';
31+
import { RUNTIME_FIELD_OPTIONS } from './field_options';
3032

3133
const { toInt } = fieldFormatters;
3234
const { emptyField, containsCharsField, numberGreaterThanField, isJsonField } = fieldValidators;
@@ -185,6 +187,52 @@ export const PARAMETERS_DEFINITION: { [key in ParameterName]: ParameterDefinitio
185187
},
186188
schema: t.string,
187189
},
190+
runtime_type: {
191+
fieldConfig: {
192+
type: FIELD_TYPES.COMBO_BOX,
193+
label: i18n.translate('xpack.idxMgmt.mappingsEditor.parameters.runtimeTypeLabel', {
194+
defaultMessage: 'Type',
195+
}),
196+
defaultValue: 'keyword',
197+
deserializer: (fieldType: RuntimeType | undefined) => {
198+
if (typeof fieldType === 'string' && Boolean(fieldType)) {
199+
const label =
200+
RUNTIME_FIELD_OPTIONS.find(({ value }) => value === fieldType)?.label ?? fieldType;
201+
return [
202+
{
203+
label,
204+
value: fieldType,
205+
},
206+
];
207+
}
208+
return [];
209+
},
210+
serializer: (value: ComboBoxOption[]) => (value.length === 0 ? '' : value[0].value),
211+
},
212+
schema: t.string,
213+
},
214+
script: {
215+
fieldConfig: {
216+
defaultValue: '',
217+
type: FIELD_TYPES.TEXT,
218+
label: i18n.translate('xpack.idxMgmt.mappingsEditor.parameters.painlessScriptLabel', {
219+
defaultMessage: 'Script',
220+
}),
221+
validations: [
222+
{
223+
validator: emptyField(
224+
i18n.translate(
225+
'xpack.idxMgmt.mappingsEditor.parameters.validations.scriptIsRequiredErrorMessage',
226+
{
227+
defaultMessage: 'Script must emit() a value.',
228+
}
229+
)
230+
),
231+
},
232+
],
233+
},
234+
schema: t.string,
235+
},
188236
store: {
189237
fieldConfig: {
190238
type: FIELD_TYPES.CHECKBOX,

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

+21-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,27 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
export * from './utils';
7+
export {
8+
getUniqueId,
9+
getChildFieldsName,
10+
getFieldMeta,
11+
getTypeLabelFromField,
12+
getFieldConfig,
13+
getTypeMetaFromSource,
14+
normalize,
15+
deNormalize,
16+
updateFieldsPathAfterFieldNameChange,
17+
getAllChildFields,
18+
getAllDescendantAliases,
19+
getFieldAncestors,
20+
filterTypesForMultiField,
21+
filterTypesForNonRootFields,
22+
getMaxNestedDepth,
23+
buildFieldTreeFromIds,
24+
shouldDeleteChildFieldsAfterTypeChange,
25+
canUseMappingsEditor,
26+
stripUndefinedValues,
27+
} from './utils';
828

929
export * from './serializers';
1030

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

+47-2
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
jest.mock('../constants', () => ({ MAIN_DATA_TYPE_DEFINITION: {} }));
7+
jest.mock('../constants', () => {
8+
const { TYPE_DEFINITION } = jest.requireActual('../constants');
9+
return { MAIN_DATA_TYPE_DEFINITION: {}, TYPE_DEFINITION };
10+
});
811

9-
import { stripUndefinedValues } from './utils';
12+
import { stripUndefinedValues, getTypeLabelFromField } from './utils';
1013

1114
describe('utils', () => {
1215
describe('stripUndefinedValues()', () => {
@@ -53,4 +56,46 @@ describe('utils', () => {
5356
expect(stripUndefinedValues(dataIN)).toEqual(dataOUT);
5457
});
5558
});
59+
60+
describe('getTypeLabelFromField()', () => {
61+
test('returns an unprocessed label for non-runtime fields', () => {
62+
expect(
63+
getTypeLabelFromField({
64+
name: 'testField',
65+
type: 'keyword',
66+
})
67+
).toBe('Keyword');
68+
});
69+
70+
test(`returns a label prepended with 'Other' for unrecognized fields`, () => {
71+
expect(
72+
getTypeLabelFromField({
73+
name: 'testField',
74+
// @ts-ignore
75+
type: 'hyperdrive',
76+
})
77+
).toBe('Other: hyperdrive');
78+
});
79+
80+
test("returns a label prepended with 'Runtime' for runtime fields", () => {
81+
expect(
82+
getTypeLabelFromField({
83+
name: 'testField',
84+
type: 'runtime',
85+
runtime_type: 'keyword',
86+
})
87+
).toBe('Runtime Keyword');
88+
});
89+
90+
test("returns a label prepended with 'Runtime Other' for unrecognized runtime fields", () => {
91+
expect(
92+
getTypeLabelFromField({
93+
name: 'testField',
94+
type: 'runtime',
95+
// @ts-ignore
96+
runtime_type: 'hyperdrive',
97+
})
98+
).toBe('Runtime Other: hyperdrive');
99+
});
100+
});
56101
});

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

+17-2
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,23 @@ export const getFieldMeta = (field: Field, isMultiField?: boolean): FieldMeta =>
7171
};
7272
};
7373

74-
export const getTypeLabelFromType = (type: DataType) =>
75-
TYPE_DEFINITION[type] ? TYPE_DEFINITION[type].label : `${TYPE_DEFINITION.other.label}: ${type}`;
74+
const getTypeLabel = (type?: DataType): string => {
75+
return type && TYPE_DEFINITION[type]
76+
? TYPE_DEFINITION[type].label
77+
: `${TYPE_DEFINITION.other.label}: ${type}`;
78+
};
79+
80+
export const getTypeLabelFromField = (field: Field) => {
81+
const { type, runtime_type: runtimeType } = field;
82+
const typeLabel = getTypeLabel(type);
83+
84+
if (type === 'runtime') {
85+
const runtimeTypeLabel = getTypeLabel(runtimeType);
86+
return `${typeLabel} ${runtimeTypeLabel}`;
87+
}
88+
89+
return typeLabel;
90+
};
7691

7792
export const getFieldConfig = <T = unknown>(
7893
param: ParameterName,

‎x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts

+2
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,5 @@ export {
5151
OnJsonEditorUpdateHandler,
5252
GlobalFlyout,
5353
} from '../../../../../../../src/plugins/es_ui_shared/public';
54+
55+
export { CodeEditor } from '../../../../../../../src/plugins/kibana_react/public';

‎x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { ReactNode } from 'react';
88
import { GenericObject } from './mappings_editor';
99

1010
import { FieldConfig } from '../shared_imports';
11-
import { PARAMETERS_DEFINITION } from '../constants';
11+
import { PARAMETERS_DEFINITION, RUNTIME_FIELD_TYPES } from '../constants';
1212

1313
export interface DataTypeDefinition {
1414
label: string;
@@ -19,6 +19,7 @@ export interface DataTypeDefinition {
1919
};
2020
subTypes?: { label: string; types: SubType[] };
2121
description?: () => ReactNode;
22+
isBeta?: boolean;
2223
}
2324

2425
export interface ParameterDefinition {
@@ -35,6 +36,7 @@ export interface ParameterDefinition {
3536
}
3637

3738
export type MainType =
39+
| 'runtime'
3840
| 'text'
3941
| 'keyword'
4042
| 'numeric'
@@ -74,6 +76,8 @@ export type SubType = NumericType | RangeType;
7476

7577
export type DataType = MainType | SubType;
7678

79+
export type RuntimeType = typeof RUNTIME_FIELD_TYPES[number];
80+
7781
export type NumericType =
7882
| 'long'
7983
| 'integer'
@@ -152,6 +156,8 @@ export type ParameterName =
152156
| 'depth_limit'
153157
| 'relations'
154158
| 'max_shingle_size'
159+
| 'runtime_type'
160+
| 'script'
155161
| 'value'
156162
| 'meta';
157163

@@ -169,6 +175,7 @@ export interface Fields {
169175
interface FieldBasic {
170176
name: string;
171177
type: DataType;
178+
runtime_type?: DataType;
172179
subType?: SubType;
173180
properties?: { [key: string]: Omit<Field, 'name'> };
174181
fields?: { [key: string]: Omit<Field, 'name'> };

‎x-pack/plugins/index_management/public/application/index.tsx

+20-13
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { render, unmountComponentAtNode } from 'react-dom';
1111
import { CoreStart } from '../../../../../src/core/public';
1212

1313
import { API_BASE_PATH } from '../../common';
14-
import { GlobalFlyout } from '../shared_imports';
14+
import { createKibanaReactContext, GlobalFlyout } from '../shared_imports';
1515

1616
import { AppContextProvider, AppDependencies } from './app_context';
1717
import { App } from './app';
@@ -30,7 +30,12 @@ export const renderApp = (
3030

3131
const { i18n, docLinks, notifications, application } = core;
3232
const { Context: I18nContext } = i18n;
33-
const { services, history, setBreadcrumbs } = dependencies;
33+
const { services, history, setBreadcrumbs, uiSettings } = dependencies;
34+
35+
// uiSettings is required by the CodeEditor component used to edit runtime field Painless scripts.
36+
const { Provider: KibanaReactContextProvider } = createKibanaReactContext({
37+
uiSettings,
38+
});
3439

3540
const componentTemplateProviderValues = {
3641
httpClient: services.httpService.httpClient,
@@ -44,17 +49,19 @@ export const renderApp = (
4449

4550
render(
4651
<I18nContext>
47-
<Provider store={indexManagementStore(services)}>
48-
<AppContextProvider value={dependencies}>
49-
<MappingsEditorProvider>
50-
<ComponentTemplatesProvider value={componentTemplateProviderValues}>
51-
<GlobalFlyoutProvider>
52-
<App history={history} />
53-
</GlobalFlyoutProvider>
54-
</ComponentTemplatesProvider>
55-
</MappingsEditorProvider>
56-
</AppContextProvider>
57-
</Provider>
52+
<KibanaReactContextProvider>
53+
<Provider store={indexManagementStore(services)}>
54+
<AppContextProvider value={dependencies}>
55+
<MappingsEditorProvider>
56+
<ComponentTemplatesProvider value={componentTemplateProviderValues}>
57+
<GlobalFlyoutProvider>
58+
<App history={history} />
59+
</GlobalFlyoutProvider>
60+
</ComponentTemplatesProvider>
61+
</MappingsEditorProvider>
62+
</AppContextProvider>
63+
</Provider>
64+
</KibanaReactContextProvider>
5865
</I18nContext>,
5966
elem
6067
);

‎x-pack/plugins/index_management/public/application/mount_management_section.ts

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export async function mountManagementSection(
4141
fatalErrors,
4242
application,
4343
chrome: { docTitle },
44+
uiSettings,
4445
} = core;
4546

4647
docTitle.change(PLUGIN.getI18nName(i18n));
@@ -60,6 +61,7 @@ export async function mountManagementSection(
6061
services,
6162
history,
6263
setBreadcrumbs,
64+
uiSettings,
6365
};
6466

6567
const unmountAppCallback = renderApp(element, { core, dependencies: appDependencies });

‎x-pack/plugins/index_management/public/shared_imports.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,7 @@ export {
4444

4545
export { isJSON } from '../../../../src/plugins/es_ui_shared/static/validators/string';
4646

47-
export { reactRouterNavigate } from '../../../../src/plugins/kibana_react/public';
47+
export {
48+
createKibanaReactContext,
49+
reactRouterNavigate,
50+
} from '../../../../src/plugins/kibana_react/public';

0 commit comments

Comments
 (0)
Please sign in to comment.