From 84ee07c8d9592f43abd9049bf73d5d115471e393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 29 Jun 2021 16:54:07 +0100 Subject: [PATCH 01/60] Add runtimeObject to indexPattern --- .../data/common/index_patterns/constants.ts | 1 + .../index_patterns/index_pattern.ts | 121 ++++++++++++++++-- .../index_patterns/index_patterns.ts | 9 +- .../data/common/index_patterns/types.ts | 28 +++- .../field_editor_flyout_content_container.tsx | 41 ++---- .../public/shared_imports.ts | 2 +- .../public/types.ts | 18 +-- 7 files changed, 159 insertions(+), 61 deletions(-) diff --git a/src/plugins/data/common/index_patterns/constants.ts b/src/plugins/data/common/index_patterns/constants.ts index 67e266dbd84a2..3e7b2c636c8da 100644 --- a/src/plugins/data/common/index_patterns/constants.ts +++ b/src/plugins/data/common/index_patterns/constants.ts @@ -14,6 +14,7 @@ export const RUNTIME_FIELD_TYPES = [ 'ip', 'boolean', 'geo_point', + 'object', ] as const; /** diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 48bcdf6982b67..2dc6c2bac5710 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -8,7 +8,12 @@ import _, { each, reject } from 'lodash'; import { FieldAttrs, FieldAttrSet, IndexPatternAttributes } from '../..'; -import type { RuntimeField } from '../types'; +import type { + KibanaRuntimeField, + EsRuntimeField, + RuntimeObject, + RuntimeObjectWithSubFields, +} from '../types'; import { DuplicateField } from '../../../../kibana_utils/common'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES, IIndexPattern, IFieldType } from '../../../common'; @@ -77,7 +82,8 @@ export class IndexPattern implements IIndexPattern { private shortDotsEnable: boolean = false; private fieldFormats: FieldFormatsStartCommon; private fieldAttrs: FieldAttrs; - private runtimeFieldMap: Record; + private runtimeFieldMap: Record; + private runtimeObjectMap: Record; /** * prevents errors when index pattern exists before indices @@ -121,6 +127,7 @@ export class IndexPattern implements IIndexPattern { this.intervalName = spec.intervalName; this.allowNoIndex = spec.allowNoIndex || false; this.runtimeFieldMap = spec.runtimeFieldMap || {}; + this.runtimeObjectMap = spec.runtimeObjectMap || {}; } /** @@ -219,6 +226,7 @@ export class IndexPattern implements IIndexPattern { type: this.type, fieldFormats: this.fieldFormatMap, runtimeFieldMap: this.runtimeFieldMap, + runtimeObjectMap: this.runtimeObjectMap, fieldAttrs: this.fieldAttrs, intervalName: this.intervalName, allowNoIndex: this.allowNoIndex, @@ -329,6 +337,7 @@ export class IndexPattern implements IIndexPattern { : JSON.stringify(this.fieldFormatMap); const fieldAttrs = this.getFieldAttrs(); const runtimeFieldMap = this.runtimeFieldMap; + const runtimeObjectMap = this.runtimeObjectMap; return { fieldAttrs: fieldAttrs ? JSON.stringify(fieldAttrs) : undefined, @@ -342,6 +351,7 @@ export class IndexPattern implements IIndexPattern { typeMeta: JSON.stringify(this.typeMeta ?? {}), allowNoIndex: this.allowNoIndex ? this.allowNoIndex : undefined, runtimeFieldMap: runtimeFieldMap ? JSON.stringify(runtimeFieldMap) : undefined, + runtimeObjectMap: runtimeObjectMap ? JSON.stringify(runtimeObjectMap) : undefined, }; } @@ -369,22 +379,34 @@ export class IndexPattern implements IIndexPattern { * @param name Field name * @param runtimeField Runtime field definition */ - addRuntimeField(name: string, runtimeField: RuntimeField) { + addRuntimeField(name: string, runtimeField: KibanaRuntimeField) { + const { type, script, customLabel, format, popularity } = runtimeField; + + const esRuntimeField = { type, script }; + const existingField = this.getFieldByName(name); if (existingField) { - existingField.runtimeField = runtimeField; + existingField.runtimeField = esRuntimeField; } else { this.fields.add({ name, - runtimeField, + runtimeField: esRuntimeField, type: castEsToKbnFieldTypeName(runtimeField.type), aggregatable: true, searchable: true, - count: 0, + count: popularity ?? 0, readFromDocValues: false, }); } - this.runtimeFieldMap[name] = runtimeField; + this.runtimeFieldMap[name] = esRuntimeField; + + this.setFieldCustomLabel(name, customLabel); + + if (format) { + this.setFieldFormat(name, format); + } else { + this.deleteFieldFormat(name); + } } /** @@ -399,7 +421,7 @@ export class IndexPattern implements IIndexPattern { * Returns runtime field if exists * @param name */ - getRuntimeField(name: string): RuntimeField | null { + getRuntimeField(name: string): EsRuntimeField | null { return this.runtimeFieldMap[name] ?? null; } @@ -407,7 +429,7 @@ export class IndexPattern implements IIndexPattern { * Replaces all existing runtime fields with new fields * @param newFields */ - replaceAllRuntimeFields(newFields: Record) { + replaceAllRuntimeFields(newFields: Record) { const oldRuntimeFieldNames = Object.keys(this.runtimeFieldMap); oldRuntimeFieldNames.forEach((name) => { this.removeRuntimeField(name); @@ -436,6 +458,87 @@ export class IndexPattern implements IIndexPattern { delete this.runtimeFieldMap[name]; } + /** + * Add a runtime object and its subFields to the fields list + * @param name - The runtime object name + * @param runtimeObject - The runtime object definition + */ + addRuntimeObject(name: string, runtimeObject: RuntimeObjectWithSubFields) { + this.removeRuntimeObject(name); + + const { script, subFields } = runtimeObject; + + for (const [subFieldName, subField] of Object.entries(subFields)) { + this.addRuntimeField(subFieldName, { ...subField, parent: name }); + } + + this.runtimeObjectMap[name] = { name, script, subFields: Object.keys(subFields) }; + } + + /** + * Returns runtime object if exists + * @param name + */ + getRuntimeObject(name: string): RuntimeObject | null { + return this.runtimeObjectMap[name] ?? null; + } + + /** + * Returns runtime object (if exists) with its subFields + * @param name + */ + getRuntimeObjectWithSubFields(name: string): RuntimeObjectWithSubFields | null { + const existingRuntimeObject = this.runtimeObjectMap[name]; + + if (!existingRuntimeObject) { + return null; + } + + const subFields = existingRuntimeObject.subFields.reduce((acc, subFieldName) => { + const field = this.getFieldByName(subFieldName); + + if (!field) { + // This condition should never happen + return acc; + } + + const runtimeField: KibanaRuntimeField = { + type: 'object', + parent: name, + customLabel: field.customLabel, + popularity: field.count, + format: this.getFormatterForFieldNoDefault(field.name)?.toJSON(), + }; + + return { + ...acc, + [subFieldName]: runtimeField, + }; + }, {} as Record); + + return { + ...existingRuntimeObject, + subFields, + }; + } + + /** + * Remove a runtime object with its associated subFields + * @param name - Object runtime name to remove + */ + removeRuntimeObject(name: string) { + const existingRuntimeObject = this.getRuntimeObject(name); + + if (!!existingRuntimeObject) { + // Remove all previous subFields + for (const subFieldName of existingRuntimeObject.subFields) { + this.removeRuntimeField(subFieldName); + } + + delete this.runtimeObjectMap[name]; + } + } + /** * Get formatter for a given field name. Return undefined if none exists * @param field diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index 74f11badbb411..e3f3f5e5f8b4b 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -11,7 +11,7 @@ import { PublicMethodsOf } from '@kbn/utility-types'; import { INDEX_PATTERN_SAVED_OBJECT_TYPE, SavedObjectsClientCommon } from '../..'; import { createIndexPatternCache } from '.'; -import type { RuntimeField } from '../types'; +import type { EsRuntimeField, RuntimeObject } from '../types'; import { IndexPattern } from './index_pattern'; import { createEnsureDefaultIndexPattern, @@ -380,6 +380,7 @@ export class IndexPatternsService { sourceFilters, fieldFormatMap, runtimeFieldMap, + runtimeObjectMap, typeMeta, type, fieldAttrs, @@ -392,9 +393,12 @@ export class IndexPatternsService { const parsedFieldFormatMap = fieldFormatMap ? JSON.parse(fieldFormatMap) : {}; const parsedFields: FieldSpec[] = fields ? JSON.parse(fields) : []; const parsedFieldAttrs: FieldAttrs = fieldAttrs ? JSON.parse(fieldAttrs) : {}; - const parsedRuntimeFieldMap: Record = runtimeFieldMap + const parsedRuntimeFieldMap: Record = runtimeFieldMap ? JSON.parse(runtimeFieldMap) : {}; + const parsedRuntimeObjectMap: Record = runtimeObjectMap + ? JSON.parse(runtimeObjectMap) + : {}; return { id, @@ -410,6 +414,7 @@ export class IndexPatternsService { fieldAttrs: parsedFieldAttrs, allowNoIndex, runtimeFieldMap: parsedRuntimeFieldMap, + runtimeObjectMap: parsedRuntimeObjectMap, }; }; diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index c326e75aca415..1b84a9877ac10 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -19,13 +19,33 @@ import { FieldFormat } from '../../../field_formats/common'; export type FieldFormatMap = Record; export type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; -export interface RuntimeField { + +export interface EsRuntimeField { type: RuntimeType; script?: { source: string; }; } +export interface KibanaRuntimeField extends EsRuntimeField { + format?: SerializedFieldFormat; + customLabel?: string; + popularity?: number; + parent?: string; +} + +export interface RuntimeObject { + name: string; + script: { + source: string; + }; + subFields: string[]; +} + +export type RuntimeObjectWithSubFields = Omit & { + subFields: Record; +}; + /** * @deprecated * IIndexPattern allows for an IndexPattern OR an index pattern saved object @@ -63,6 +83,7 @@ export interface IndexPatternAttributes { fieldFormatMap?: string; fieldAttrs?: string; runtimeFieldMap?: string; + runtimeObjectMap?: string; /** * prevents errors when index pattern exists before indices */ @@ -202,7 +223,7 @@ export interface FieldSpec extends IndexPatternFieldBase { readFromDocValues?: boolean; indexed?: boolean; customLabel?: string; - runtimeField?: RuntimeField; + runtimeField?: EsRuntimeField; // not persisted shortDotsEnable?: boolean; isMapped?: boolean; @@ -235,7 +256,8 @@ export interface IndexPatternSpec { typeMeta?: TypeMeta; type?: string; fieldFormats?: Record; - runtimeFieldMap?: Record; + runtimeFieldMap?: Record; + runtimeObjectMap?: Record; fieldAttrs?: FieldAttrs; allowNoIndex?: boolean; } diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index cf2b29bbc97e8..e5c7528b08aed 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -15,7 +15,6 @@ import { IndexPatternField, IndexPattern, DataPublicPluginStart, - RuntimeType, UsageCollectionStart, } from '../shared_imports'; import type { Field, PluginStart, InternalFieldType } from '../types'; @@ -121,22 +120,18 @@ export const FieldEditorFlyoutContentContainer = ({ async (updatedField: Field) => { setIsSaving(true); - const { script } = updatedField; - if (fieldTypeToProcess === 'runtime') { try { usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'save_runtime'); // eslint-disable-next-line no-empty } catch {} + // rename an existing runtime field if (field?.name && field.name !== updatedField.name) { indexPattern.removeRuntimeField(field.name); } - indexPattern.addRuntimeField(updatedField.name, { - type: updatedField.type as RuntimeType, - script, - }); + indexPattern.addRuntimeField(updatedField.name, updatedField); } else { try { usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'save_concrete'); @@ -144,32 +139,18 @@ export const FieldEditorFlyoutContentContainer = ({ } catch {} } - const editedField = indexPattern.getFieldByName(updatedField.name); - try { - if (!editedField) { - throw new Error( - `Unable to find field named '${updatedField.name}' on index pattern '${indexPattern.title}'` - ); - } + await indexPatternService.updateSavedObject(indexPattern); - indexPattern.setFieldCustomLabel(updatedField.name, updatedField.customLabel); - editedField.count = updatedField.popularity || 0; - if (updatedField.format) { - indexPattern.setFieldFormat(updatedField.name, updatedField.format); - } else { - indexPattern.deleteFieldFormat(updatedField.name); - } - - await indexPatternService.updateSavedObject(indexPattern).then(() => { - const message = i18n.translate('indexPatternFieldEditor.deleteField.savedHeader', { - defaultMessage: "Saved '{fieldName}'", - values: { fieldName: updatedField.name }, - }); - notifications.toasts.addSuccess(message); - setIsSaving(false); - onSave(editedField); + const message = i18n.translate('indexPatternFieldEditor.deleteField.savedHeader', { + defaultMessage: "Saved '{fieldName}'", + values: { fieldName: updatedField.name }, }); + notifications.toasts.addSuccess(message); + setIsSaving(false); + + const editedField = indexPattern.getFieldByName(updatedField.name); + onSave(editedField!); } catch (e) { const title = i18n.translate('indexPatternFieldEditor.save.errorTitle', { defaultMessage: 'Failed to save field changes', diff --git a/src/plugins/index_pattern_field_editor/public/shared_imports.ts b/src/plugins/index_pattern_field_editor/public/shared_imports.ts index 2827928d1c060..0760a1e8c609c 100644 --- a/src/plugins/index_pattern_field_editor/public/shared_imports.ts +++ b/src/plugins/index_pattern_field_editor/public/shared_imports.ts @@ -10,7 +10,7 @@ export { IndexPattern, IndexPatternField, DataPublicPluginStart } from '../../da export { UsageCollectionStart } from '../../usage_collection/public'; -export { RuntimeType, RuntimeField, KBN_FIELD_TYPES, ES_FIELD_TYPES } from '../../data/common'; +export { RuntimeType, EsRuntimeField, KBN_FIELD_TYPES, ES_FIELD_TYPES } from '../../data/common'; export { createKibanaReactContext, toMountPoint, CodeEditor } from '../../kibana_react/public'; diff --git a/src/plugins/index_pattern_field_editor/public/types.ts b/src/plugins/index_pattern_field_editor/public/types.ts index f7efc9d82fc48..131eb80af7c48 100644 --- a/src/plugins/index_pattern_field_editor/public/types.ts +++ b/src/plugins/index_pattern_field_editor/public/types.ts @@ -8,12 +8,7 @@ import { FunctionComponent } from 'react'; -import { - DataPublicPluginStart, - RuntimeField, - RuntimeType, - UsageCollectionStart, -} from './shared_imports'; +import { DataPublicPluginStart, EsRuntimeField, UsageCollectionStart } from './shared_imports'; import { OpenFieldEditorOptions } from './open_editor'; import { OpenFieldDeleteModalOptions } from './open_delete_modal'; import { FormatEditorServiceSetup, FormatEditorServiceStart } from './service'; @@ -43,10 +38,8 @@ export interface StartPlugins { export type InternalFieldType = 'concrete' | 'runtime'; -export interface Field { +export interface Field extends EsRuntimeField { name: string; - type: RuntimeField['type'] | string; - script?: RuntimeField['script']; customLabel?: string; popularity?: number; format?: FieldFormatConfig; @@ -57,13 +50,6 @@ export interface FieldFormatConfig { params?: { [key: string]: any }; } -export interface EsRuntimeField { - type: RuntimeType | string; - script?: { - source: string; - }; -} - export type CloseEditor = () => void; export type FieldPreviewContext = From c136163d7b816af794c3e1cee04635524a947db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 29 Jun 2021 17:29:56 +0100 Subject: [PATCH 02/60] Add parent param to IndexPatternField --- .../common/index_patterns/fields/index_pattern_field.ts | 8 ++++++-- .../common/index_patterns/index_patterns/index_pattern.ts | 7 ++++--- src/plugins/data/common/index_patterns/types.ts | 1 + 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts index 0c7a668087da8..cc354a1a90367 100644 --- a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts +++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { RuntimeField } from '../types'; +import type { EsRuntimeField } from '../types'; import { KbnFieldType, getKbnFieldType, castEsToKbnFieldTypeName } from '../../kbn_field_types'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import type { IFieldType } from './types'; @@ -41,10 +41,14 @@ export class IndexPatternField implements IFieldType { return this.spec.runtimeField; } - public set runtimeField(runtimeField: RuntimeField | undefined) { + public set runtimeField(runtimeField: EsRuntimeField | undefined) { this.spec.runtimeField = runtimeField; } + public get parent() { + return this.spec.parent; + } + /** * Script field code */ diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 2dc6c2bac5710..6c9ee630f85b8 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -380,7 +380,7 @@ export class IndexPattern implements IIndexPattern { * @param runtimeField Runtime field definition */ addRuntimeField(name: string, runtimeField: KibanaRuntimeField) { - const { type, script, customLabel, format, popularity } = runtimeField; + const { type, script, parent, customLabel, format, popularity } = runtimeField; const esRuntimeField = { type, script }; @@ -396,6 +396,7 @@ export class IndexPattern implements IIndexPattern { searchable: true, count: popularity ?? 0, readFromDocValues: false, + parent, }); } this.runtimeFieldMap[name] = esRuntimeField; @@ -469,7 +470,7 @@ export class IndexPattern implements IIndexPattern { const { script, subFields } = runtimeObject; for (const [subFieldName, subField] of Object.entries(subFields)) { - this.addRuntimeField(subFieldName, { ...subField, parent: name }); + this.addRuntimeField(`${name}.${subFieldName}`, { ...subField, parent: name }); } this.runtimeObjectMap[name] = { name, script, subFields: Object.keys(subFields) }; @@ -532,7 +533,7 @@ export class IndexPattern implements IIndexPattern { if (!!existingRuntimeObject) { // Remove all previous subFields for (const subFieldName of existingRuntimeObject.subFields) { - this.removeRuntimeField(subFieldName); + this.removeRuntimeField(`${name}.${subFieldName}`); } delete this.runtimeObjectMap[name]; diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 1b84a9877ac10..2e4a62334d1a4 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -227,6 +227,7 @@ export interface FieldSpec extends IndexPatternFieldBase { // not persisted shortDotsEnable?: boolean; isMapped?: boolean; + parent?: string; } export type IndexPatternFieldMap = Record; From a3765792035acb4f80296b71758953def5862203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 29 Jun 2021 17:30:19 +0100 Subject: [PATCH 03/60] Block edit and delete of object subFields --- .../public/open_delete_modal.tsx | 10 ++++++++++ .../index_pattern_field_editor/public/open_editor.tsx | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx index 72dbb76863353..a5bd9efcd65c8 100644 --- a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx @@ -40,6 +40,16 @@ export const getFieldDeleteModalOpener = ({ indexPatternService, usageCollection, }: Dependencies) => (options: OpenFieldDeleteModalOptions): CloseEditor => { + if (typeof options.fieldName === 'string') { + const fieldToDelete = options.ctx.indexPattern.getFieldByName(options.fieldName); + if (fieldToDelete?.runtimeField && fieldToDelete.parent !== undefined) { + console.log( // eslint-disable-line + 'Need to display a modal to indicate that this field needs to be deleted through its parent.' + ); + return () => undefined; + } + } + const { overlays, notifications } = core; let overlayRef: OverlayRef | null = null; diff --git a/src/plugins/index_pattern_field_editor/public/open_editor.tsx b/src/plugins/index_pattern_field_editor/public/open_editor.tsx index 946e666bf8205..31c660baf9545 100644 --- a/src/plugins/index_pattern_field_editor/public/open_editor.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_editor.tsx @@ -104,6 +104,13 @@ export const getFieldEditorOpener = ({ const fieldTypeToProcess: InternalFieldType = isNewRuntimeField || isExistingRuntimeField ? 'runtime' : 'concrete'; + if (field?.runtimeField && field?.parent !== undefined) { + console.log( // eslint-disable-line + 'Need to display a modal to indicate that this field needs to be edited through its parent.' + ); + return closeEditor; + } + overlayRef = overlays.openFlyout( toMountPoint( From 14816d8be4119701212dc72127de9f2c0f97a90a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Wed, 30 Jun 2021 14:05:27 +0100 Subject: [PATCH 04/60] Handle computedRuntimeFields and computedRuntimeObjects --- .../index_patterns/fields/field_list.ts | 5 +- .../fields/index_pattern_field.ts | 4 -- .../index_patterns/index_pattern.ts | 59 ++++++++++++++--- .../data/common/index_patterns/types.ts | 2 +- .../field_editor_flyout_content_container.tsx | 65 +++++++++++++++---- .../public/open_delete_modal.tsx | 2 +- .../public/open_editor.tsx | 8 +-- 7 files changed, 113 insertions(+), 32 deletions(-) diff --git a/src/plugins/data/common/index_patterns/fields/field_list.ts b/src/plugins/data/common/index_patterns/fields/field_list.ts index 40503e6dbc1b1..2183b023b62a3 100644 --- a/src/plugins/data/common/index_patterns/fields/field_list.ts +++ b/src/plugins/data/common/index_patterns/fields/field_list.ts @@ -15,7 +15,7 @@ import { IndexPattern } from '../index_patterns'; type FieldMap = Map; export interface IIndexPatternFieldList extends Array { - add(field: FieldSpec): void; + add(field: FieldSpec): IndexPatternField; getAll(): IndexPatternField[]; getByName(name: IndexPatternField['name']): IndexPatternField | undefined; getByType(type: IndexPatternField['type']): IndexPatternField[]; @@ -57,11 +57,12 @@ export const fieldList = ( public readonly getByType = (type: IndexPatternField['type']) => [ ...(this.groups.get(type) || new Map()).values(), ]; - public readonly add = (field: FieldSpec) => { + public readonly add = (field: FieldSpec): IndexPatternField => { const newField = new IndexPatternField({ ...field, shortDotsEnable }); this.push(newField); this.setByName(newField); this.setByGroup(newField); + return newField; }; public readonly remove = (field: IFieldType) => { diff --git a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts index cc354a1a90367..b6b845e3b1727 100644 --- a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts +++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts @@ -45,10 +45,6 @@ export class IndexPatternField implements IFieldType { this.spec.runtimeField = runtimeField; } - public get parent() { - return this.spec.parent; - } - /** * Script field code */ diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 6c9ee630f85b8..8c28eb76c8836 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -202,14 +202,45 @@ export class IndexPattern implements IIndexPattern { }; }); + const runtimeFields = { + ...this.getComputedRuntimeFields(), + ...this.getComputedRuntimeObjects(), + }; + return { storedFields: ['*'], scriptFields, docvalueFields, - runtimeFields: this.runtimeFieldMap, + runtimeFields, }; } + private getComputedRuntimeFields() { + // Runtime fields which are **not** created from a parent object + return Object.entries(this.runtimeFieldMap).reduce((acc, [name, field]) => { + const { type, script, parent } = field; + if (parent !== undefined) { + return acc; + } + return { + ...acc, + [name]: { type, script }, + }; + }, {}); + } + + private getComputedRuntimeObjects() { + // Only return the script and set its type to 'object' + return Object.entries(this.runtimeObjectMap).reduce((acc, [name, runtimeObject]) => { + const { script } = runtimeObject; + return { + ...acc, + // [name]: { type: 'object', script }, // 'object' not supported yet ES side + [name]: { type: 'keyword', script }, // Temp to make demo work + }; + }, {}); + } + /** * Create static representation of index pattern */ @@ -379,16 +410,18 @@ export class IndexPattern implements IIndexPattern { * @param name Field name * @param runtimeField Runtime field definition */ - addRuntimeField(name: string, runtimeField: KibanaRuntimeField) { + addRuntimeField(name: string, runtimeField: KibanaRuntimeField): IndexPatternField { const { type, script, parent, customLabel, format, popularity } = runtimeField; - const esRuntimeField = { type, script }; + const esRuntimeField = { type, script, parent }; + let fieldCreated: IndexPatternField; const existingField = this.getFieldByName(name); if (existingField) { existingField.runtimeField = esRuntimeField; + fieldCreated = existingField; } else { - this.fields.add({ + fieldCreated = this.fields.add({ name, runtimeField: esRuntimeField, type: castEsToKbnFieldTypeName(runtimeField.type), @@ -396,7 +429,6 @@ export class IndexPattern implements IIndexPattern { searchable: true, count: popularity ?? 0, readFromDocValues: false, - parent, }); } this.runtimeFieldMap[name] = esRuntimeField; @@ -408,6 +440,8 @@ export class IndexPattern implements IIndexPattern { } else { this.deleteFieldFormat(name); } + + return fieldCreated; } /** @@ -464,16 +498,25 @@ export class IndexPattern implements IIndexPattern { * @param name - The runtime object name * @param runtimeObject - The runtime object definition */ - addRuntimeObject(name: string, runtimeObject: RuntimeObjectWithSubFields) { + addRuntimeObject(name: string, runtimeObject: RuntimeObjectWithSubFields): IndexPatternField[] { this.removeRuntimeObject(name); const { script, subFields } = runtimeObject; + const fieldsCreated: IndexPatternField[] = []; + for (const [subFieldName, subField] of Object.entries(subFields)) { - this.addRuntimeField(`${name}.${subFieldName}`, { ...subField, parent: name }); + const field = this.addRuntimeField(`${name}.${subFieldName}`, { ...subField, parent: name }); + fieldsCreated.push(field); } - this.runtimeObjectMap[name] = { name, script, subFields: Object.keys(subFields) }; + this.runtimeObjectMap[name] = { + name, + script, + subFields: Object.keys(subFields), + }; + + return fieldsCreated; } /** diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 2e4a62334d1a4..fbc8f9a72f7c4 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -25,13 +25,13 @@ export interface EsRuntimeField { script?: { source: string; }; + parent?: string; } export interface KibanaRuntimeField extends EsRuntimeField { format?: SerializedFieldFormat; customLabel?: string; popularity?: number; - parent?: string; } export interface RuntimeObject { diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index e5c7528b08aed..d3a789b96f96c 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -29,7 +29,7 @@ import { FieldPreviewProvider } from './preview'; export interface Props { /** Handler for the "save" footer button */ - onSave: (field: IndexPatternField) => void; + onSave: (field: IndexPatternField[]) => void; /** Handler for the "cancel" footer button */ onCancel: () => void; onMounted?: FieldEditorFlyoutContentProps['onMounted']; @@ -116,6 +116,49 @@ export const FieldEditorFlyoutContentContainer = ({ [apiService, search, notifications] ); + const saveRuntimeObject = useCallback( + (updatedField: Field): IndexPatternField[] => { + if (field?.type !== undefined && field?.type !== 'object') { + // A previous non object runtime field is now an object + indexPattern.removeRuntimeField(field.name); + } else if (field?.name && field.name !== updatedField.name) { + // rename an existing runtime object + indexPattern.removeRuntimeObject(field.name); + } + + // --- Temporary hack to create a runtime object --- + const runtimeName = 'aaaObject'; + const tempRuntimeObject = { + name: runtimeName, + script: updatedField.script!, + subFields: { + field_a: updatedField, + field_b: updatedField, + field_c: updatedField, + }, + }; + return indexPattern.addRuntimeObject(runtimeName, tempRuntimeObject); + // --- end temporary hack --- + }, + [field?.name, field?.type, indexPattern] + ); + + const saveRuntimeField = useCallback( + (updatedField: Field): [IndexPatternField] => { + if (field?.type !== undefined && field?.type === 'object') { + // A previous object runtime field is now an object + indexPattern.removeRuntimeObject(field.name); + } else if (field?.name && field.name !== updatedField.name) { + // rename an existing runtime field + indexPattern.removeRuntimeField(field.name); + } + + const fieldCreated = indexPattern.addRuntimeField(updatedField.name, updatedField); + return [fieldCreated]; + }, + [field?.name, field?.type, indexPattern] + ); + const saveField = useCallback( async (updatedField: Field) => { setIsSaving(true); @@ -125,13 +168,6 @@ export const FieldEditorFlyoutContentContainer = ({ usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'save_runtime'); // eslint-disable-next-line no-empty } catch {} - - // rename an existing runtime field - if (field?.name && field.name !== updatedField.name) { - indexPattern.removeRuntimeField(field.name); - } - - indexPattern.addRuntimeField(updatedField.name, updatedField); } else { try { usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'save_concrete'); @@ -140,6 +176,11 @@ export const FieldEditorFlyoutContentContainer = ({ } try { + const editedFields: IndexPatternField[] = + // updatedField.type === 'object' + // --> always "true" to demo the creation of runtime objects + true ? saveRuntimeObject(updatedField) : saveRuntimeField(updatedField); + await indexPatternService.updateSavedObject(indexPattern); const message = i18n.translate('indexPatternFieldEditor.deleteField.savedHeader', { @@ -147,10 +188,9 @@ export const FieldEditorFlyoutContentContainer = ({ values: { fieldName: updatedField.name }, }); notifications.toasts.addSuccess(message); - setIsSaving(false); - const editedField = indexPattern.getFieldByName(updatedField.name); - onSave(editedField!); + setIsSaving(false); + onSave(editedFields); } catch (e) { const title = i18n.translate('indexPatternFieldEditor.save.errorTitle', { defaultMessage: 'Failed to save field changes', @@ -165,8 +205,9 @@ export const FieldEditorFlyoutContentContainer = ({ indexPatternService, notifications, fieldTypeToProcess, - field?.name, usageCollection, + saveRuntimeField, + saveRuntimeObject, ] ); diff --git a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx index a5bd9efcd65c8..24736d0bcf8fe 100644 --- a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx @@ -44,7 +44,7 @@ export const getFieldDeleteModalOpener = ({ const fieldToDelete = options.ctx.indexPattern.getFieldByName(options.fieldName); if (fieldToDelete?.runtimeField && fieldToDelete.parent !== undefined) { console.log( // eslint-disable-line - 'Need to display a modal to indicate that this field needs to be deleted through its parent.' + 'TODO: display a modal to indicate that this field needs to be deleted through its parent.' ); return () => undefined; } diff --git a/src/plugins/index_pattern_field_editor/public/open_editor.tsx b/src/plugins/index_pattern_field_editor/public/open_editor.tsx index 31c660baf9545..c72325ba25cc7 100644 --- a/src/plugins/index_pattern_field_editor/public/open_editor.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_editor.tsx @@ -28,7 +28,7 @@ export interface OpenFieldEditorOptions { ctx: { indexPattern: IndexPattern; }; - onSave?: (field: IndexPatternField) => void; + onSave?: (field: IndexPatternField[]) => void; fieldName?: string; } @@ -80,7 +80,7 @@ export const getFieldEditorOpener = ({ } }; - const onSaveField = (updatedField: IndexPatternField) => { + const onSaveField = (updatedField: IndexPatternField[]) => { closeEditor(); if (onSave) { @@ -104,9 +104,9 @@ export const getFieldEditorOpener = ({ const fieldTypeToProcess: InternalFieldType = isNewRuntimeField || isExistingRuntimeField ? 'runtime' : 'concrete'; - if (field?.runtimeField && field?.parent !== undefined) { + if (field?.runtimeField?.parent !== undefined) { console.log( // eslint-disable-line - 'Need to display a modal to indicate that this field needs to be edited through its parent.' + 'TODO: display a modal to indicate that this field needs to be edited through its parent.' ); return closeEditor; } From ce5148bf4cb0cf435c6b2540dbc9483dc7d7ae0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Wed, 30 Jun 2021 14:17:57 +0100 Subject: [PATCH 05/60] Refactor interface naming, using EnhancedRuntimeField for Kibana --- .../index_patterns/fields/index_pattern_field.ts | 4 ++-- .../index_patterns/index_pattern.ts | 16 ++++++++-------- .../index_patterns/index_patterns.ts | 4 ++-- src/plugins/data/common/index_patterns/types.ts | 10 +++++----- .../components/field_editor_flyout_content.tsx | 5 +++-- .../public/lib/runtime_field_validation.ts | 5 ++--- .../public/open_delete_modal.tsx | 2 +- .../public/shared_imports.ts | 2 +- .../index_pattern_field_editor/public/types.ts | 4 ++-- 9 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts index b6b845e3b1727..0c7a668087da8 100644 --- a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts +++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { EsRuntimeField } from '../types'; +import type { RuntimeField } from '../types'; import { KbnFieldType, getKbnFieldType, castEsToKbnFieldTypeName } from '../../kbn_field_types'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import type { IFieldType } from './types'; @@ -41,7 +41,7 @@ export class IndexPatternField implements IFieldType { return this.spec.runtimeField; } - public set runtimeField(runtimeField: EsRuntimeField | undefined) { + public set runtimeField(runtimeField: RuntimeField | undefined) { this.spec.runtimeField = runtimeField; } diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 8c28eb76c8836..265faac2d18e0 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -9,8 +9,8 @@ import _, { each, reject } from 'lodash'; import { FieldAttrs, FieldAttrSet, IndexPatternAttributes } from '../..'; import type { - KibanaRuntimeField, - EsRuntimeField, + EnhancedRuntimeField, + RuntimeField, RuntimeObject, RuntimeObjectWithSubFields, } from '../types'; @@ -82,7 +82,7 @@ export class IndexPattern implements IIndexPattern { private shortDotsEnable: boolean = false; private fieldFormats: FieldFormatsStartCommon; private fieldAttrs: FieldAttrs; - private runtimeFieldMap: Record; + private runtimeFieldMap: Record; private runtimeObjectMap: Record; /** @@ -410,7 +410,7 @@ export class IndexPattern implements IIndexPattern { * @param name Field name * @param runtimeField Runtime field definition */ - addRuntimeField(name: string, runtimeField: KibanaRuntimeField): IndexPatternField { + addRuntimeField(name: string, runtimeField: EnhancedRuntimeField): IndexPatternField { const { type, script, parent, customLabel, format, popularity } = runtimeField; const esRuntimeField = { type, script, parent }; @@ -456,7 +456,7 @@ export class IndexPattern implements IIndexPattern { * Returns runtime field if exists * @param name */ - getRuntimeField(name: string): EsRuntimeField | null { + getRuntimeField(name: string): RuntimeField | null { return this.runtimeFieldMap[name] ?? null; } @@ -464,7 +464,7 @@ export class IndexPattern implements IIndexPattern { * Replaces all existing runtime fields with new fields * @param newFields */ - replaceAllRuntimeFields(newFields: Record) { + replaceAllRuntimeFields(newFields: Record) { const oldRuntimeFieldNames = Object.keys(this.runtimeFieldMap); oldRuntimeFieldNames.forEach((name) => { this.removeRuntimeField(name); @@ -546,7 +546,7 @@ export class IndexPattern implements IIndexPattern { return acc; } - const runtimeField: KibanaRuntimeField = { + const runtimeField: EnhancedRuntimeField = { type: 'object', parent: name, customLabel: field.customLabel, @@ -558,7 +558,7 @@ export class IndexPattern implements IIndexPattern { ...acc, [subFieldName]: runtimeField, }; - }, {} as Record); + }, {} as Record); return { ...existingRuntimeObject, diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index e3f3f5e5f8b4b..1a5064f6b200f 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -11,7 +11,7 @@ import { PublicMethodsOf } from '@kbn/utility-types'; import { INDEX_PATTERN_SAVED_OBJECT_TYPE, SavedObjectsClientCommon } from '../..'; import { createIndexPatternCache } from '.'; -import type { EsRuntimeField, RuntimeObject } from '../types'; +import type { RuntimeField, RuntimeObject } from '../types'; import { IndexPattern } from './index_pattern'; import { createEnsureDefaultIndexPattern, @@ -393,7 +393,7 @@ export class IndexPatternsService { const parsedFieldFormatMap = fieldFormatMap ? JSON.parse(fieldFormatMap) : {}; const parsedFields: FieldSpec[] = fields ? JSON.parse(fields) : []; const parsedFieldAttrs: FieldAttrs = fieldAttrs ? JSON.parse(fieldAttrs) : {}; - const parsedRuntimeFieldMap: Record = runtimeFieldMap + const parsedRuntimeFieldMap: Record = runtimeFieldMap ? JSON.parse(runtimeFieldMap) : {}; const parsedRuntimeObjectMap: Record = runtimeObjectMap diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index fbc8f9a72f7c4..43fe0b9453350 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -20,7 +20,7 @@ export type FieldFormatMap = Record; export type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; -export interface EsRuntimeField { +export interface RuntimeField { type: RuntimeType; script?: { source: string; @@ -28,7 +28,7 @@ export interface EsRuntimeField { parent?: string; } -export interface KibanaRuntimeField extends EsRuntimeField { +export interface EnhancedRuntimeField extends RuntimeField { format?: SerializedFieldFormat; customLabel?: string; popularity?: number; @@ -43,7 +43,7 @@ export interface RuntimeObject { } export type RuntimeObjectWithSubFields = Omit & { - subFields: Record; + subFields: Record; }; /** @@ -223,7 +223,7 @@ export interface FieldSpec extends IndexPatternFieldBase { readFromDocValues?: boolean; indexed?: boolean; customLabel?: string; - runtimeField?: EsRuntimeField; + runtimeField?: RuntimeField; // not persisted shortDotsEnable?: boolean; isMapped?: boolean; @@ -257,7 +257,7 @@ export interface IndexPatternSpec { typeMeta?: TypeMeta; type?: string; fieldFormats?: Record; - runtimeFieldMap?: Record; + runtimeFieldMap?: Record; runtimeObjectMap?: Record; fieldAttrs?: FieldAttrs; allowNoIndex?: boolean; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx index 19015aa9d0d10..ef7759de7108d 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx @@ -20,9 +20,10 @@ import { EuiText, } from '@elastic/eui'; -import type { Field, EsRuntimeField } from '../types'; +import type { Field } from '../types'; import { RuntimeFieldPainlessError } from '../lib'; import { euiFlyoutClassname } from '../constants'; +import type { RuntimeField } from '../shared_imports'; import { FlyoutPanels } from './flyout_panels'; import { useFieldEditorContext } from './field_editor_context'; import { FieldEditor, FieldEditorFormState } from './field_editor/field_editor'; @@ -56,7 +57,7 @@ export interface Props { */ onCancel: () => void; /** Handler to validate the script */ - runtimeFieldValidator: (field: EsRuntimeField) => Promise; + runtimeFieldValidator: (field: RuntimeField) => Promise; /** Optional field to process */ field?: Field; isSavingField: boolean; diff --git a/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.ts b/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.ts index 789c4f7fa71fc..5553e8d583417 100644 --- a/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.ts +++ b/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.ts @@ -7,8 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import { DataPublicPluginStart } from '../shared_imports'; -import type { EsRuntimeField } from '../types'; +import { DataPublicPluginStart, RuntimeField } from '../shared_imports'; export interface RuntimeFieldPainlessError { message: string; @@ -95,7 +94,7 @@ export const parseEsError = ( export const getRuntimeFieldValidator = ( index: string, searchService: DataPublicPluginStart['search'] -) => async (runtimeField: EsRuntimeField) => { +) => async (runtimeField: RuntimeField) => { return await searchService .search({ params: { diff --git a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx index 24736d0bcf8fe..05afb1772d0ca 100644 --- a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx @@ -42,7 +42,7 @@ export const getFieldDeleteModalOpener = ({ }: Dependencies) => (options: OpenFieldDeleteModalOptions): CloseEditor => { if (typeof options.fieldName === 'string') { const fieldToDelete = options.ctx.indexPattern.getFieldByName(options.fieldName); - if (fieldToDelete?.runtimeField && fieldToDelete.parent !== undefined) { + if (fieldToDelete?.runtimeField?.parent !== undefined) { console.log( // eslint-disable-line 'TODO: display a modal to indicate that this field needs to be deleted through its parent.' ); diff --git a/src/plugins/index_pattern_field_editor/public/shared_imports.ts b/src/plugins/index_pattern_field_editor/public/shared_imports.ts index 0760a1e8c609c..2827928d1c060 100644 --- a/src/plugins/index_pattern_field_editor/public/shared_imports.ts +++ b/src/plugins/index_pattern_field_editor/public/shared_imports.ts @@ -10,7 +10,7 @@ export { IndexPattern, IndexPatternField, DataPublicPluginStart } from '../../da export { UsageCollectionStart } from '../../usage_collection/public'; -export { RuntimeType, EsRuntimeField, KBN_FIELD_TYPES, ES_FIELD_TYPES } from '../../data/common'; +export { RuntimeType, RuntimeField, KBN_FIELD_TYPES, ES_FIELD_TYPES } from '../../data/common'; export { createKibanaReactContext, toMountPoint, CodeEditor } from '../../kibana_react/public'; diff --git a/src/plugins/index_pattern_field_editor/public/types.ts b/src/plugins/index_pattern_field_editor/public/types.ts index 131eb80af7c48..2cc718b3a9178 100644 --- a/src/plugins/index_pattern_field_editor/public/types.ts +++ b/src/plugins/index_pattern_field_editor/public/types.ts @@ -8,7 +8,7 @@ import { FunctionComponent } from 'react'; -import { DataPublicPluginStart, EsRuntimeField, UsageCollectionStart } from './shared_imports'; +import { DataPublicPluginStart, RuntimeField, UsageCollectionStart } from './shared_imports'; import { OpenFieldEditorOptions } from './open_editor'; import { OpenFieldDeleteModalOptions } from './open_delete_modal'; import { FormatEditorServiceSetup, FormatEditorServiceStart } from './service'; @@ -38,7 +38,7 @@ export interface StartPlugins { export type InternalFieldType = 'concrete' | 'runtime'; -export interface Field extends EsRuntimeField { +export interface Field extends RuntimeField { name: string; customLabel?: string; popularity?: number; From 6df961a790e533bb5c5f5cb0278c3151a6ab876e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Wed, 30 Jun 2021 14:28:15 +0100 Subject: [PATCH 06/60] Cleanup --- .../index_patterns/index_patterns/index_pattern.ts | 14 +++++++------- .../field_editor_flyout_content_container.tsx | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 265faac2d18e0..1e2b3c295e136 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -410,28 +410,28 @@ export class IndexPattern implements IIndexPattern { * @param name Field name * @param runtimeField Runtime field definition */ - addRuntimeField(name: string, runtimeField: EnhancedRuntimeField): IndexPatternField { - const { type, script, parent, customLabel, format, popularity } = runtimeField; + addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): IndexPatternField { + const { type, script, parent, customLabel, format, popularity } = enhancedRuntimeField; - const esRuntimeField = { type, script, parent }; + const runtimeField: RuntimeField = { type, script, parent }; let fieldCreated: IndexPatternField; const existingField = this.getFieldByName(name); if (existingField) { - existingField.runtimeField = esRuntimeField; + existingField.runtimeField = runtimeField; fieldCreated = existingField; } else { fieldCreated = this.fields.add({ name, - runtimeField: esRuntimeField, - type: castEsToKbnFieldTypeName(runtimeField.type), + runtimeField, + type: castEsToKbnFieldTypeName(type), aggregatable: true, searchable: true, count: popularity ?? 0, readFromDocValues: false, }); } - this.runtimeFieldMap[name] = esRuntimeField; + this.runtimeFieldMap[name] = runtimeField; this.setFieldCustomLabel(name, customLabel); diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index d3a789b96f96c..9fb334a2db435 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -119,7 +119,7 @@ export const FieldEditorFlyoutContentContainer = ({ const saveRuntimeObject = useCallback( (updatedField: Field): IndexPatternField[] => { if (field?.type !== undefined && field?.type !== 'object') { - // A previous non object runtime field is now an object + // A previous runtime field is now a runtime object indexPattern.removeRuntimeField(field.name); } else if (field?.name && field.name !== updatedField.name) { // rename an existing runtime object @@ -146,7 +146,7 @@ export const FieldEditorFlyoutContentContainer = ({ const saveRuntimeField = useCallback( (updatedField: Field): [IndexPatternField] => { if (field?.type !== undefined && field?.type === 'object') { - // A previous object runtime field is now an object + // A previous runtime object is now a runtime field indexPattern.removeRuntimeObject(field.name); } else if (field?.name && field.name !== updatedField.name) { // rename an existing runtime field From 94e08e75e0467883ce475d6356bd6114a5de467d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Wed, 30 Jun 2021 14:35:39 +0100 Subject: [PATCH 07/60] Add comment for getComputedRuntimeObjects() --- .../index_patterns/index_pattern.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 1e2b3c295e136..479480721f251 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -229,13 +229,37 @@ export class IndexPattern implements IIndexPattern { }, {}); } + /** + * This method will need to be updated once ES support the "object" type + * We will need to add an aditional "fields" prop with the subFields. + * + * This is what ES will be expecting for runtime objects + * https://github.com/elastic/elasticsearch/issues/68203 + * + * { + * "objectName": { + * "type": "object", + * "script": "emit(...)" // script that emits multiple values + * "fields": { // map of the subFields + * "field_1": { + * "type": "ip" + * }, + * "field_2": { + * "type": "ip" + * }, + * } + * } + * } + * + * @returns A map of runtime objects + */ private getComputedRuntimeObjects() { // Only return the script and set its type to 'object' return Object.entries(this.runtimeObjectMap).reduce((acc, [name, runtimeObject]) => { const { script } = runtimeObject; return { ...acc, - // [name]: { type: 'object', script }, // 'object' not supported yet ES side + // [name]: { type: 'object', script, fields: { ... } }, // 'object' not supported yet ES side [name]: { type: 'keyword', script }, // Temp to make demo work }; }, {}); From 6209b5d10803c01bdfc4ce4d019bcee0b2ed1c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Wed, 30 Jun 2021 16:30:04 +0100 Subject: [PATCH 08/60] Don't allow saving runtime objects without subfields --- .../common/index_patterns/index_patterns/index_pattern.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 479480721f251..e6aa5a1661329 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -523,6 +523,10 @@ export class IndexPattern implements IIndexPattern { * @param runtimeObject - The runtime object definition */ addRuntimeObject(name: string, runtimeObject: RuntimeObjectWithSubFields): IndexPatternField[] { + if (!runtimeObject.subFields || Object.keys(runtimeObject.subFields).length === 0) { + throw new Error(`Can't save runtime object [name = ${name}] without subfields.`); + } + this.removeRuntimeObject(name); const { script, subFields } = runtimeObject; From d922d418a2d48c214104a79dd201a1343a40e738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 23 Aug 2021 12:58:12 +0100 Subject: [PATCH 09/60] Fix TS issues --- src/plugins/data/common/index_patterns/constants.ts | 2 +- src/plugins/data/public/index.ts | 1 + .../__jest__/client_integration/field_editor.test.tsx | 2 +- .../client_integration/field_editor_flyout_content.test.ts | 4 ++-- .../client_integration/field_editor_flyout_preview.test.ts | 4 ++-- .../index_pattern_field_editor/public/lib/serialization.ts | 4 ++-- .../ml/public/application/components/data_grid/common.ts | 4 ++-- x-pack/plugins/transform/common/api_schemas/common.ts | 1 + 8 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/plugins/data/common/index_patterns/constants.ts b/src/plugins/data/common/index_patterns/constants.ts index 3e7b2c636c8da..43e41832e1770 100644 --- a/src/plugins/data/common/index_patterns/constants.ts +++ b/src/plugins/data/common/index_patterns/constants.ts @@ -14,7 +14,7 @@ export const RUNTIME_FIELD_TYPES = [ 'ip', 'boolean', 'geo_point', - 'object', + 'composite', ] as const; /** diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 0602f51889a6c..2ae0831ec8c28 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -80,6 +80,7 @@ export { AggregationRestrictions, IndexPatternType, IndexPatternListItem, + RuntimeType, } from '../common'; export { DuplicateIndexPatternError } from '../common/index_patterns/errors'; diff --git a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx index 4a4c42f69fc8e..16803533bf7fd 100644 --- a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx +++ b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx @@ -98,7 +98,7 @@ describe('', () => { test('should accept a defaultValue and onChange prop to forward the form state', async () => { const field = { name: 'foo', - type: 'date', + type: 'date' as const, script: { source: 'emit("hello")' }, }; diff --git a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts index 9b00ff762fe8f..35ffb7f58b6c7 100644 --- a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts +++ b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts @@ -33,7 +33,7 @@ describe('', () => { test('should allow a field to be provided', async () => { const field = { name: 'foo', - type: 'ip', + type: 'ip' as const, script: { source: 'emit("hello world")', }, @@ -50,7 +50,7 @@ describe('', () => { test('should accept an "onSave" prop', async () => { const field = { name: 'foo', - type: 'date', + type: 'date' as const, script: { source: 'test=123' }, }; const onSave: jest.Mock = jest.fn(); diff --git a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts index 65089bc24317b..33e74aec1bb83 100644 --- a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts +++ b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts @@ -280,7 +280,7 @@ describe('Field editor Preview panel', () => { test('should **not** display an empty prompt editing a document with a script', async () => { const field = { name: 'foo', - type: 'ip', + type: 'ip' as const, script: { source: 'emit("hello world")', }, @@ -303,7 +303,7 @@ describe('Field editor Preview panel', () => { test('should **not** display an empty prompt editing a document with format defined', async () => { const field = { name: 'foo', - type: 'ip', + type: 'ip' as const, format: { id: 'upper', params: {}, diff --git a/src/plugins/index_pattern_field_editor/public/lib/serialization.ts b/src/plugins/index_pattern_field_editor/public/lib/serialization.ts index 8a0a47e07c9c9..8400138d12d39 100644 --- a/src/plugins/index_pattern_field_editor/public/lib/serialization.ts +++ b/src/plugins/index_pattern_field_editor/public/lib/serialization.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { IndexPatternField, IndexPattern } from '../shared_imports'; +import { IndexPatternField, IndexPattern, RuntimeType } from '../shared_imports'; import type { Field } from '../types'; export const deserializeField = ( @@ -19,7 +19,7 @@ export const deserializeField = ( return { name: field.name, - type: field?.esTypes ? field.esTypes[0] : 'keyword', + type: (field?.esTypes ? field.esTypes[0] : 'keyword') as RuntimeType, script: field.runtimeField ? field.runtimeField.script : undefined, customLabel: field.customLabel, popularity: field.count, diff --git a/x-pack/plugins/ml/public/application/components/data_grid/common.ts b/x-pack/plugins/ml/public/application/components/data_grid/common.ts index a64594e86a757..8690720814e96 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/common.ts +++ b/x-pack/plugins/ml/public/application/components/data_grid/common.ts @@ -6,7 +6,6 @@ */ import moment from 'moment-timezone'; -import { estypes } from '@elastic/elasticsearch'; import { useEffect, useMemo } from 'react'; import { @@ -24,6 +23,7 @@ import { IFieldType, ES_FIELD_TYPES, KBN_FIELD_TYPES, + RuntimeType, } from '../../../../../../../src/plugins/data/public'; import { DEFAULT_RESULTS_FIELD } from '../../../../common/constants/data_frame_analytics'; @@ -181,7 +181,7 @@ export const getDataGridSchemasFromFieldTypes = (fieldTypes: FieldTypes, results export const NON_AGGREGATABLE = 'non-aggregatable'; export const getDataGridSchemaFromESFieldType = ( - fieldType: ES_FIELD_TYPES | undefined | estypes.MappingRuntimeField['type'] + fieldType: ES_FIELD_TYPES | undefined | RuntimeType ): string | undefined => { // Built-in values are ['boolean', 'currency', 'datetime', 'numeric', 'json'] // To fall back to the default string schema it needs to be undefined. diff --git a/x-pack/plugins/transform/common/api_schemas/common.ts b/x-pack/plugins/transform/common/api_schemas/common.ts index 5354598dc2475..fdefa3a8c4ad9 100644 --- a/x-pack/plugins/transform/common/api_schemas/common.ts +++ b/x-pack/plugins/transform/common/api_schemas/common.ts @@ -68,6 +68,7 @@ export const runtimeMappingsSchema = schema.maybe( schema.literal('ip'), schema.literal('boolean'), schema.literal('geo_point'), + schema.literal('composite'), ]), script: schema.maybe( schema.oneOf([ From 9b865edfef1d21366b8b7b35bb578e501d12c13e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 23 Aug 2021 13:02:11 +0100 Subject: [PATCH 10/60] Update jest snapshot --- .../index_patterns/__snapshots__/index_pattern.test.ts.snap | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap index 1c6b57f70071b..84ad0e4524399 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap +++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap @@ -784,6 +784,7 @@ Object { "type": "keyword", }, }, + "runtimeObjectMap": Object {}, "sourceFilters": undefined, "timeFieldName": "time", "title": "title", From 58be09d9d3cc1ce658fd9027e61e1b2a405cf9b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 23 Aug 2021 16:10:47 +0100 Subject: [PATCH 11/60] Add "composite" option and update Preview to support it --- .../preview/field_preview_context.tsx | 106 +++++++++++++++--- .../server/routes/field_preview.ts | 1 + 2 files changed, 89 insertions(+), 18 deletions(-) diff --git a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx index e1fc4b05883f4..0d2b402995508 100644 --- a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx @@ -317,6 +317,47 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { [indexPattern, search] ); + const setSingleFieldPreview = useCallback( + (fieldName: string, values: unknown[]) => { + const [value] = values; + const formattedValue = valueFormatter(value); + + setPreviewResponse({ + fields: [{ key: fieldName, value, formattedValue }], + error: null, + }); + }, + [valueFormatter] + ); + + const setCompositeFieldPreview = useCallback( + (compositeName: string, compositeValues: Record) => { + if (typeof compositeValues !== 'object') { + return; + } + + const fields = Object.entries(compositeValues).map(([key, values]) => { + // The Painless _execute API returns the composite field values under a map. + // Each of the key is prefixed with "composite_field." (e.g. "composite_field.field1: ['value']") + const { 1: fieldName } = key.split('composite_field.'); + const [value] = values; + const formattedValue = valueFormatter(value); + + return { + key: `${compositeName ?? ''}.${fieldName}`, + value, + formattedValue, + }; + }); + + setPreviewResponse({ + fields, + error: null, + }); + }, + [valueFormatter] + ); + const updatePreview = useCallback(async () => { setLastExecutePainlessReqParams({ type: params.type, @@ -371,13 +412,11 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { error: { code: 'PAINLESS_SCRIPT_ERROR', error: parseEsError(error, true) ?? fallBackError }, }); } else { - const [value] = values; - const formattedValue = valueFormatter(value); - - setPreviewResponse({ - fields: [{ key: params.name!, value, formattedValue }], - error: null, - }); + if (!Array.isArray(values)) { + setCompositeFieldPreview(params.name!, values); + return; + } + setSingleFieldPreview(params.name!, values); } }, [ needToUpdatePreview, @@ -386,7 +425,8 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { currentDocId, getFieldPreview, notifications.toasts, - valueFormatter, + setSingleFieldPreview, + setCompositeFieldPreview, ]); const goToNextDoc = useCallback(() => { @@ -543,24 +583,54 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { }, [currentDocument, updateParams]); /** - * Whenever the name or the format changes we immediately update the preview + * Whenever the name changes we immediately update the preview */ useEffect(() => { setPreviewResponse((prev) => { - const { - fields: { 0: field }, - } = prev; + const { fields } = prev; - const nextValue = - script === null && Boolean(document) - ? get(document, name ?? '') // When there is no script we read the value from _source - : field?.value; + return { + ...prev, + // fields: [{ ...field, key: name ?? '', value: nextValue, formattedValue }], + fields: fields.map((field) => { + let key: string = name ?? ''; + if (type === 'composite' && name !== null) { + const { 1: fieldName } = field.key.split('.'); + key = `${name}.${fieldName}`; + } + return { + ...field, + key, + }; + }), + }; + }); + }, [name, type]); - const formattedValue = valueFormatter(nextValue); + /** + * Whenever the format changes we immediately update the preview + */ + useEffect(() => { + setPreviewResponse((prev) => { + const { fields } = prev; return { ...prev, - fields: [{ ...field, key: name ?? '', value: nextValue, formattedValue }], + // fields: [{ ...field, key: name ?? '', value: nextValue, formattedValue }], + fields: fields.map((field) => { + const nextValue = + script === null && Boolean(document) + ? get(document, name ?? '') // When there is no script we read the value from _source + : field?.value; + + const formattedValue = valueFormatter(nextValue); + + return { + ...field, + value: nextValue, + formattedValue, + }; + }), }; }); }, [name, script, document, valueFormatter]); diff --git a/src/plugins/index_pattern_field_editor/server/routes/field_preview.ts b/src/plugins/index_pattern_field_editor/server/routes/field_preview.ts index 238701904e22c..ff66d711adf96 100644 --- a/src/plugins/index_pattern_field_editor/server/routes/field_preview.ts +++ b/src/plugins/index_pattern_field_editor/server/routes/field_preview.ts @@ -24,6 +24,7 @@ const bodySchema = schema.object({ schema.literal('ip_field'), schema.literal('keyword_field'), schema.literal('long_field'), + schema.literal('composite_field'), ]), document: schema.object({}, { unknowns: 'allow' }), }); From f6527d46009c5ac92a87d80acd1bf5edd0708cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 23 Aug 2021 16:32:04 +0100 Subject: [PATCH 12/60] Refactor code to use "composite" instead of "object" --- .../index_patterns/index_pattern.ts | 93 ++++++++++--------- .../data/common/index_patterns/types.ts | 8 +- .../field_editor_flyout_content_container.tsx | 44 ++++----- 3 files changed, 75 insertions(+), 70 deletions(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index e6aa5a1661329..6f573d55ab841 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -11,8 +11,8 @@ import { FieldAttrs, FieldAttrSet, IndexPatternAttributes } from '../..'; import type { EnhancedRuntimeField, RuntimeField, - RuntimeObject, - RuntimeObjectWithSubFields, + RuntimeComposite, + RuntimeCompositeWithSubFields, } from '../types'; import { DuplicateField } from '../../../../kibana_utils/common'; @@ -83,7 +83,7 @@ export class IndexPattern implements IIndexPattern { private fieldFormats: FieldFormatsStartCommon; private fieldAttrs: FieldAttrs; private runtimeFieldMap: Record; - private runtimeObjectMap: Record; + private runtimeCompositeMap: Record; /** * prevents errors when index pattern exists before indices @@ -127,7 +127,7 @@ export class IndexPattern implements IIndexPattern { this.intervalName = spec.intervalName; this.allowNoIndex = spec.allowNoIndex || false; this.runtimeFieldMap = spec.runtimeFieldMap || {}; - this.runtimeObjectMap = spec.runtimeObjectMap || {}; + this.runtimeCompositeMap = spec.runtimeCompositeMap || {}; } /** @@ -204,7 +204,7 @@ export class IndexPattern implements IIndexPattern { const runtimeFields = { ...this.getComputedRuntimeFields(), - ...this.getComputedRuntimeObjects(), + ...this.getComputedRuntimeComposites(), }; return { @@ -216,7 +216,7 @@ export class IndexPattern implements IIndexPattern { } private getComputedRuntimeFields() { - // Runtime fields which are **not** created from a parent object + // Runtime fields which are **not** created from a parent composite return Object.entries(this.runtimeFieldMap).reduce((acc, [name, field]) => { const { type, script, parent } = field; if (parent !== undefined) { @@ -230,15 +230,15 @@ export class IndexPattern implements IIndexPattern { } /** - * This method will need to be updated once ES support the "object" type + * This method will need to be updated once ES support the "composite" type * We will need to add an aditional "fields" prop with the subFields. * - * This is what ES will be expecting for runtime objects + * This is what ES will be expecting for runtime composites * https://github.com/elastic/elasticsearch/issues/68203 * * { - * "objectName": { - * "type": "object", + * "compositeName": { + * "type": "composite", * "script": "emit(...)" // script that emits multiple values * "fields": { // map of the subFields * "field_1": { @@ -251,15 +251,15 @@ export class IndexPattern implements IIndexPattern { * } * } * - * @returns A map of runtime objects + * @returns A map of runtime composites */ - private getComputedRuntimeObjects() { - // Only return the script and set its type to 'object' - return Object.entries(this.runtimeObjectMap).reduce((acc, [name, runtimeObject]) => { - const { script } = runtimeObject; + private getComputedRuntimeComposites() { + // Only return the script and set its type to 'composite' + return Object.entries(this.runtimeCompositeMap).reduce((acc, [name, runtimeComposite]) => { + const { script } = runtimeComposite; return { ...acc, - // [name]: { type: 'object', script, fields: { ... } }, // 'object' not supported yet ES side + // [name]: { type: 'composite', script, fields: { ... } }, [name]: { type: 'keyword', script }, // Temp to make demo work }; }, {}); @@ -281,7 +281,7 @@ export class IndexPattern implements IIndexPattern { type: this.type, fieldFormats: this.fieldFormatMap, runtimeFieldMap: this.runtimeFieldMap, - runtimeObjectMap: this.runtimeObjectMap, + runtimeCompositeMap: this.runtimeCompositeMap, fieldAttrs: this.fieldAttrs, intervalName: this.intervalName, allowNoIndex: this.allowNoIndex, @@ -392,7 +392,7 @@ export class IndexPattern implements IIndexPattern { : JSON.stringify(this.fieldFormatMap); const fieldAttrs = this.getFieldAttrs(); const runtimeFieldMap = this.runtimeFieldMap; - const runtimeObjectMap = this.runtimeObjectMap; + const runtimeCompositeMap = this.runtimeCompositeMap; return { fieldAttrs: fieldAttrs ? JSON.stringify(fieldAttrs) : undefined, @@ -406,7 +406,7 @@ export class IndexPattern implements IIndexPattern { typeMeta: JSON.stringify(this.typeMeta ?? {}), allowNoIndex: this.allowNoIndex ? this.allowNoIndex : undefined, runtimeFieldMap: runtimeFieldMap ? JSON.stringify(runtimeFieldMap) : undefined, - runtimeObjectMap: runtimeObjectMap ? JSON.stringify(runtimeObjectMap) : undefined, + runtimeCompositeMap: runtimeCompositeMap ? JSON.stringify(runtimeCompositeMap) : undefined, }; } @@ -518,18 +518,21 @@ export class IndexPattern implements IIndexPattern { } /** - * Add a runtime object and its subFields to the fields list - * @param name - The runtime object name - * @param runtimeObject - The runtime object definition + * Add a runtime composite and its subFields to the fields list + * @param name - The runtime composite name + * @param runtimeComposite - The runtime composite definition */ - addRuntimeObject(name: string, runtimeObject: RuntimeObjectWithSubFields): IndexPatternField[] { - if (!runtimeObject.subFields || Object.keys(runtimeObject.subFields).length === 0) { - throw new Error(`Can't save runtime object [name = ${name}] without subfields.`); + addRuntimeComposite( + name: string, + runtimeComposite: RuntimeCompositeWithSubFields + ): IndexPatternField[] { + if (!runtimeComposite.subFields || Object.keys(runtimeComposite.subFields).length === 0) { + throw new Error(`Can't save runtime composite [name = ${name}] without subfields.`); } - this.removeRuntimeObject(name); + this.removeRuntimeComposite(name); - const { script, subFields } = runtimeObject; + const { script, subFields } = runtimeComposite; const fieldsCreated: IndexPatternField[] = []; @@ -538,7 +541,7 @@ export class IndexPattern implements IIndexPattern { fieldsCreated.push(field); } - this.runtimeObjectMap[name] = { + this.runtimeCompositeMap[name] = { name, script, subFields: Object.keys(subFields), @@ -548,25 +551,25 @@ export class IndexPattern implements IIndexPattern { } /** - * Returns runtime object if exists + * Returns runtime composite if exists * @param name */ - getRuntimeObject(name: string): RuntimeObject | null { - return this.runtimeObjectMap[name] ?? null; + getRuntimeComposite(name: string): RuntimeComposite | null { + return this.runtimeCompositeMap[name] ?? null; } /** - * Returns runtime object (if exists) with its subFields + * Returns runtime composite (if exists) with its subFields * @param name */ - getRuntimeObjectWithSubFields(name: string): RuntimeObjectWithSubFields | null { - const existingRuntimeObject = this.runtimeObjectMap[name]; + getRuntimeCompositeWithSubFields(name: string): RuntimeCompositeWithSubFields | null { + const existingRuntimeComposite = this.runtimeCompositeMap[name]; - if (!existingRuntimeObject) { + if (!existingRuntimeComposite) { return null; } - const subFields = existingRuntimeObject.subFields.reduce((acc, subFieldName) => { + const subFields = existingRuntimeComposite.subFields.reduce((acc, subFieldName) => { const field = this.getFieldByName(subFieldName); if (!field) { @@ -575,7 +578,7 @@ export class IndexPattern implements IIndexPattern { } const runtimeField: EnhancedRuntimeField = { - type: 'object', + type: 'composite', parent: name, customLabel: field.customLabel, popularity: field.count, @@ -589,25 +592,25 @@ export class IndexPattern implements IIndexPattern { }, {} as Record); return { - ...existingRuntimeObject, + ...existingRuntimeComposite, subFields, }; } /** - * Remove a runtime object with its associated subFields - * @param name - Object runtime name to remove + * Remove a runtime composite with its associated subFields + * @param name - Runtime composite name to remove */ - removeRuntimeObject(name: string) { - const existingRuntimeObject = this.getRuntimeObject(name); + removeRuntimeComposite(name: string) { + const existingRuntimeComposite = this.getRuntimeComposite(name); - if (!!existingRuntimeObject) { + if (!!existingRuntimeComposite) { // Remove all previous subFields - for (const subFieldName of existingRuntimeObject.subFields) { + for (const subFieldName of existingRuntimeComposite.subFields) { this.removeRuntimeField(`${name}.${subFieldName}`); } - delete this.runtimeObjectMap[name]; + delete this.runtimeCompositeMap[name]; } } diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 43fe0b9453350..43e1937f2a7ee 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -34,7 +34,7 @@ export interface EnhancedRuntimeField extends RuntimeField { popularity?: number; } -export interface RuntimeObject { +export interface RuntimeComposite { name: string; script: { source: string; @@ -42,7 +42,7 @@ export interface RuntimeObject { subFields: string[]; } -export type RuntimeObjectWithSubFields = Omit & { +export type RuntimeCompositeWithSubFields = Omit & { subFields: Record; }; @@ -83,7 +83,7 @@ export interface IndexPatternAttributes { fieldFormatMap?: string; fieldAttrs?: string; runtimeFieldMap?: string; - runtimeObjectMap?: string; + runtimeCompositeMap?: string; /** * prevents errors when index pattern exists before indices */ @@ -258,7 +258,7 @@ export interface IndexPatternSpec { type?: string; fieldFormats?: Record; runtimeFieldMap?: Record; - runtimeObjectMap?: Record; + runtimeCompositeMap?: Record; fieldAttrs?: FieldAttrs; allowNoIndex?: boolean; } diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index 9fb334a2db435..383ce14b6f718 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -116,28 +116,30 @@ export const FieldEditorFlyoutContentContainer = ({ [apiService, search, notifications] ); - const saveRuntimeObject = useCallback( + const saveCompositeRuntime = useCallback( (updatedField: Field): IndexPatternField[] => { - if (field?.type !== undefined && field?.type !== 'object') { - // A previous runtime field is now a runtime object + if (field?.type !== undefined && field?.type !== 'composite') { + // A previous runtime field is now a runtime composite indexPattern.removeRuntimeField(field.name); } else if (field?.name && field.name !== updatedField.name) { - // rename an existing runtime object - indexPattern.removeRuntimeObject(field.name); + // rename an existing runtime composite + indexPattern.removeRuntimeComposite(field.name); } + // console.log(updatedField); + return []; // --- Temporary hack to create a runtime object --- - const runtimeName = 'aaaObject'; - const tempRuntimeObject = { - name: runtimeName, - script: updatedField.script!, - subFields: { - field_a: updatedField, - field_b: updatedField, - field_c: updatedField, - }, - }; - return indexPattern.addRuntimeObject(runtimeName, tempRuntimeObject); + // const runtimeName = 'aaaObject'; + // const tempRuntimeObject = { + // name: runtimeName, + // script: updatedField.script!, + // subFields: { + // field_a: updatedField, + // field_b: updatedField, + // field_c: updatedField, + // }, + // }; + // return indexPattern.addRuntimeComposite(runtimeName, tempRuntimeObject); // --- end temporary hack --- }, [field?.name, field?.type, indexPattern] @@ -147,7 +149,7 @@ export const FieldEditorFlyoutContentContainer = ({ (updatedField: Field): [IndexPatternField] => { if (field?.type !== undefined && field?.type === 'object') { // A previous runtime object is now a runtime field - indexPattern.removeRuntimeObject(field.name); + indexPattern.removeRuntimeComposite(field.name); } else if (field?.name && field.name !== updatedField.name) { // rename an existing runtime field indexPattern.removeRuntimeField(field.name); @@ -177,9 +179,9 @@ export const FieldEditorFlyoutContentContainer = ({ try { const editedFields: IndexPatternField[] = - // updatedField.type === 'object' - // --> always "true" to demo the creation of runtime objects - true ? saveRuntimeObject(updatedField) : saveRuntimeField(updatedField); + updatedField.type === 'composite' + ? saveCompositeRuntime(updatedField) + : saveRuntimeField(updatedField); await indexPatternService.updateSavedObject(indexPattern); @@ -207,7 +209,7 @@ export const FieldEditorFlyoutContentContainer = ({ fieldTypeToProcess, usageCollection, saveRuntimeField, - saveRuntimeObject, + saveCompositeRuntime, ] ); From 3929e192eac295aafb34c9a83acc3f1ae18b7e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 24 Aug 2021 16:29:31 +0100 Subject: [PATCH 13/60] Update IndexPattern to handle composite runtime field --- .../index_patterns/index_pattern.ts | 69 ++++++++++++++----- .../index_patterns/index_patterns.ts | 14 ++-- .../data/common/index_patterns/types.ts | 6 ++ .../field_editor_flyout_content.tsx | 57 +++++++++++---- .../field_editor_flyout_content_container.tsx | 30 +++----- .../preview/field_preview_context.tsx | 55 +++++++++------ .../public/open_delete_modal.tsx | 6 +- .../public/shared_imports.ts | 8 ++- .../public/types.ts | 18 +++-- 9 files changed, 183 insertions(+), 80 deletions(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 6f573d55ab841..7c23001cac1c6 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -11,6 +11,7 @@ import { FieldAttrs, FieldAttrSet, IndexPatternAttributes } from '../..'; import type { EnhancedRuntimeField, RuntimeField, + RuntimeType, RuntimeComposite, RuntimeCompositeWithSubFields, } from '../types'; @@ -215,34 +216,42 @@ export class IndexPattern implements IIndexPattern { }; } - private getComputedRuntimeFields() { - // Runtime fields which are **not** created from a parent composite + /** + * Method to aggregate all the runtime fields which are **not** created + * from a parent composite runtime field. + * @returns Runtime fields which are **not** created from a parent composite + */ + private getComputedRuntimeFields(): Record { return Object.entries(this.runtimeFieldMap).reduce((acc, [name, field]) => { const { type, script, parent } = field; + if (parent !== undefined) { return acc; } + + const runtimeFieldRequest: RuntimeField = { + type, + script, + }; + return { ...acc, - [name]: { type, script }, + [name]: runtimeFieldRequest, }; }, {}); } /** - * This method will need to be updated once ES support the "composite" type - * We will need to add an aditional "fields" prop with the subFields. - * - * This is what ES will be expecting for runtime composites - * https://github.com/elastic/elasticsearch/issues/68203 + * This method reads all the runtime composite fields + * and merge into it all the subFields * * { * "compositeName": { * "type": "composite", * "script": "emit(...)" // script that emits multiple values - * "fields": { // map of the subFields + * "fields": { // map of subFields available in the Query * "field_1": { - * "type": "ip" + * "type": "keyword" * }, * "field_2": { * "type": "ip" @@ -251,16 +260,44 @@ export class IndexPattern implements IIndexPattern { * } * } * - * @returns A map of runtime composites + * @returns A map of runtime fields */ - private getComputedRuntimeComposites() { - // Only return the script and set its type to 'composite' + private getComputedRuntimeComposites(): Record { return Object.entries(this.runtimeCompositeMap).reduce((acc, [name, runtimeComposite]) => { - const { script } = runtimeComposite; + const { script, subFields } = runtimeComposite; + + // Aggregate all the subFields belonging to this runtimeComposite + const fields: Record = subFields.reduce( + (accFields, subFieldName) => { + const subField = this.getRuntimeField(`${name}.${subFieldName}`); + + if (!subField) { + return accFields; + } + + return { + ...accFields, + [subFieldName]: { type: subField.type }, + }; + }, + {} as Record + ); + + if (Object.keys(fields).length === 0) { + // This should never happen, but sending a composite runtime field + // with an empty "fields" will break the Query + return acc; + } + + const runtimeFieldRequest: RuntimeField = { + type: 'composite', + script, + fields, + }; + return { ...acc, - // [name]: { type: 'composite', script, fields: { ... } }, - [name]: { type: 'keyword', script }, // Temp to make demo work + [name]: runtimeFieldRequest, }; }, {}); } diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index 1a5064f6b200f..d2f22abdf3ba6 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -11,7 +11,7 @@ import { PublicMethodsOf } from '@kbn/utility-types'; import { INDEX_PATTERN_SAVED_OBJECT_TYPE, SavedObjectsClientCommon } from '../..'; import { createIndexPatternCache } from '.'; -import type { RuntimeField, RuntimeObject } from '../types'; +import type { RuntimeField, RuntimeComposite } from '../types'; import { IndexPattern } from './index_pattern'; import { createEnsureDefaultIndexPattern, @@ -380,7 +380,7 @@ export class IndexPatternsService { sourceFilters, fieldFormatMap, runtimeFieldMap, - runtimeObjectMap, + runtimeCompositeMap, typeMeta, type, fieldAttrs, @@ -396,8 +396,8 @@ export class IndexPatternsService { const parsedRuntimeFieldMap: Record = runtimeFieldMap ? JSON.parse(runtimeFieldMap) : {}; - const parsedRuntimeObjectMap: Record = runtimeObjectMap - ? JSON.parse(runtimeObjectMap) + const parsedRuntimeCompositeMap: Record = runtimeCompositeMap + ? JSON.parse(runtimeCompositeMap) : {}; return { @@ -414,7 +414,7 @@ export class IndexPatternsService { fieldAttrs: parsedFieldAttrs, allowNoIndex, runtimeFieldMap: parsedRuntimeFieldMap, - runtimeObjectMap: parsedRuntimeObjectMap, + runtimeCompositeMap: parsedRuntimeCompositeMap, }; }; @@ -496,6 +496,10 @@ export class IndexPatternsService { ? JSON.parse(savedObject.attributes.fieldFormatMap) : {}; + spec.runtimeCompositeMap = savedObject.attributes.runtimeCompositeMap + ? JSON.parse(savedObject.attributes.runtimeCompositeMap) + : {}; + const indexPattern = await this.create(spec, true); indexPattern.resetOriginalSavedObjectBody(); return indexPattern; diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 43e1937f2a7ee..1ce502a32757d 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -26,6 +26,12 @@ export interface RuntimeField { source: string; }; parent?: string; + fields?: Record< + string, + { + type: Omit; + } + >; } export interface EnhancedRuntimeField extends RuntimeField { diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx index ef7759de7108d..20ccd59ae878f 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx @@ -20,10 +20,10 @@ import { EuiText, } from '@elastic/eui'; -import type { Field } from '../types'; +import type { Field, CompositeField } from '../types'; import { RuntimeFieldPainlessError } from '../lib'; import { euiFlyoutClassname } from '../constants'; -import type { RuntimeField } from '../shared_imports'; +import type { RuntimeField, EnhancedRuntimeField } from '../shared_imports'; import { FlyoutPanels } from './flyout_panels'; import { useFieldEditorContext } from './field_editor_context'; import { FieldEditor, FieldEditorFormState } from './field_editor/field_editor'; @@ -51,7 +51,7 @@ export interface Props { /** * Handler for the "save" footer button */ - onSave: (field: Field) => void; + onSave: (field: Field | CompositeField) => void; /** * Handler for the "cancel" footer button */ @@ -79,6 +79,7 @@ const FieldEditorFlyoutContentComponent = ({ const { indexPattern } = useFieldEditorContext(); const { panel: { isVisible: isPanelVisible }, + fieldsInScript, } = useFieldPreviewContext(); const [formState, setFormState] = useState({ @@ -120,18 +121,48 @@ const FieldEditorFlyoutContentComponent = ({ return !isFormModified; }, [isFormModified]); + const addSubfieldsToField = useCallback( + (_field: Field): Field | CompositeField => { + if (_field.type === 'composite' && fieldsInScript.length > 0) { + const subFields = fieldsInScript.reduce((acc, subFieldName) => { + const subField: EnhancedRuntimeField = { + // We hardcode "keyword" and "format" for now until the UI + // lets us define them for each of the sub fields + type: 'keyword' as const, + format: _field.format, + }; + + acc[subFieldName] = subField; + return acc; + }, {} as Record); + + const updatedField: CompositeField = { + type: 'composite', + name: _field.name, + script: _field.script, + subFields, + }; + + return updatedField; + } + + return _field; + }, + [fieldsInScript] + ); + const onClickSave = useCallback(async () => { - const { isValid, data } = await submit(); - const nameChange = field?.name !== data.name; - const typeChange = field?.type !== data.type; + const { isValid, data: updatedField } = await submit(); + const nameChange = field?.name !== updatedField.name; + const typeChange = field?.type !== updatedField.type; if (isValid) { - if (data.script) { + if (updatedField.script) { setIsValidating(true); const error = await runtimeFieldValidator({ - type: data.type, - script: data.script, + type: updatedField.type, + script: updatedField.script, }); setIsValidating(false); @@ -148,10 +179,10 @@ const FieldEditorFlyoutContentComponent = ({ confirmChangeNameOrType: true, }); } else { - onSave(data); + onSave(addSubfieldsToField(updatedField)); } } - }, [onSave, submit, runtimeFieldValidator, field, isEditingExistingField]); + }, [onSave, submit, runtimeFieldValidator, field, isEditingExistingField, addSubfieldsToField]); const onClickCancel = useCallback(() => { const canClose = canCloseValidator(); @@ -167,8 +198,8 @@ const FieldEditorFlyoutContentComponent = ({ { - const { data } = await submit(); - onSave(data); + const { data: updatedField } = await submit(); + onSave(addSubfieldsToField(updatedField)); }} onCancel={() => { setModalVisibility(defaultModalVisibility); diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index 383ce14b6f718..9a00f0fb62b0d 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -17,7 +17,7 @@ import { DataPublicPluginStart, UsageCollectionStart, } from '../shared_imports'; -import type { Field, PluginStart, InternalFieldType } from '../types'; +import type { Field, PluginStart, InternalFieldType, CompositeField } from '../types'; import { pluginName } from '../constants'; import { deserializeField, getRuntimeFieldValidator, getLinks, ApiService } from '../lib'; import { @@ -117,7 +117,7 @@ export const FieldEditorFlyoutContentContainer = ({ ); const saveCompositeRuntime = useCallback( - (updatedField: Field): IndexPatternField[] => { + (updatedField: CompositeField): IndexPatternField[] => { if (field?.type !== undefined && field?.type !== 'composite') { // A previous runtime field is now a runtime composite indexPattern.removeRuntimeField(field.name); @@ -126,21 +126,13 @@ export const FieldEditorFlyoutContentContainer = ({ indexPattern.removeRuntimeComposite(field.name); } - // console.log(updatedField); - return []; - // --- Temporary hack to create a runtime object --- - // const runtimeName = 'aaaObject'; - // const tempRuntimeObject = { - // name: runtimeName, - // script: updatedField.script!, - // subFields: { - // field_a: updatedField, - // field_b: updatedField, - // field_c: updatedField, - // }, - // }; - // return indexPattern.addRuntimeComposite(runtimeName, tempRuntimeObject); - // --- end temporary hack --- + const { name, script, subFields } = updatedField; + + return indexPattern.addRuntimeComposite(name, { + name, + script: script!, + subFields, + }); }, [field?.name, field?.type, indexPattern] ); @@ -162,7 +154,7 @@ export const FieldEditorFlyoutContentContainer = ({ ); const saveField = useCallback( - async (updatedField: Field) => { + async (updatedField: Field | CompositeField) => { setIsSaving(true); if (fieldTypeToProcess === 'runtime') { @@ -180,7 +172,7 @@ export const FieldEditorFlyoutContentContainer = ({ try { const editedFields: IndexPatternField[] = updatedField.type === 'composite' - ? saveCompositeRuntime(updatedField) + ? saveCompositeRuntime(updatedField as CompositeField) : saveRuntimeField(updatedField); await indexPatternService.updateSavedObject(indexPattern); diff --git a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx index 0d2b402995508..d91cc8e25bbe8 100644 --- a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx @@ -94,6 +94,8 @@ interface Context { value: { [key: string]: boolean }; set: React.Dispatch>; }; + /** List of fields detected in the Painless script */ + fieldsInScript: string[]; } const fieldPreviewContext = createContext(undefined); @@ -157,6 +159,8 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { const [from, setFrom] = useState('cluster'); /** Map of fields pinned to the top of the list */ const [pinnedFields, setPinnedFields] = useState<{ [key: string]: boolean }>({}); + /** Array of fields detected in the script (returned by the _execute API) */ + const [fieldsInScript, setFieldsInScript] = useState([]); const { documents, currentIdx } = clusterData; const currentDocument: EsDocument | undefined = useMemo(() => documents[currentIdx], [ @@ -317,7 +321,7 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { [indexPattern, search] ); - const setSingleFieldPreview = useCallback( + const updateSingleFieldPreview = useCallback( (fieldName: string, values: unknown[]) => { const [value] = values; const formattedValue = valueFormatter(value); @@ -330,21 +334,25 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { [valueFormatter] ); - const setCompositeFieldPreview = useCallback( - (compositeName: string, compositeValues: Record) => { + const updateCompositeFieldPreview = useCallback( + (compositeName: string | null, compositeValues: Record) => { if (typeof compositeValues !== 'object') { return; } + const updatedFieldsInScript: string[] = []; + const fields = Object.entries(compositeValues).map(([key, values]) => { // The Painless _execute API returns the composite field values under a map. // Each of the key is prefixed with "composite_field." (e.g. "composite_field.field1: ['value']") const { 1: fieldName } = key.split('composite_field.'); + updatedFieldsInScript.push(fieldName); + const [value] = values; const formattedValue = valueFormatter(value); return { - key: `${compositeName ?? ''}.${fieldName}`, + key: `${compositeName ?? ''}.${fieldName}`, value, formattedValue, }; @@ -354,6 +362,8 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { fields, error: null, }); + + setFieldsInScript(updatedFieldsInScript); }, [valueFormatter] ); @@ -413,10 +423,10 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { }); } else { if (!Array.isArray(values)) { - setCompositeFieldPreview(params.name!, values); + updateCompositeFieldPreview(params.name, values); return; } - setSingleFieldPreview(params.name!, values); + updateSingleFieldPreview(params.name!, values); } }, [ needToUpdatePreview, @@ -425,8 +435,8 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { currentDocId, getFieldPreview, notifications.toasts, - setSingleFieldPreview, - setCompositeFieldPreview, + updateSingleFieldPreview, + updateCompositeFieldPreview, ]); const goToNextDoc = useCallback(() => { @@ -503,6 +513,7 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { value: pinnedFields, set: setPinnedFields, }, + fieldsInScript, }), [ previewResponse, @@ -522,6 +533,7 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { from, reset, pinnedFields, + fieldsInScript, ] ); @@ -589,20 +601,23 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { setPreviewResponse((prev) => { const { fields } = prev; + const updatedFields = fields.map((field) => { + let key: string = name ?? ''; + + if (type === 'composite') { + const { 1: fieldName } = field.key.split('.'); + key = `${name ?? ''}.${fieldName}`; + } + + return { + ...field, + key, + }; + }); + return { ...prev, - // fields: [{ ...field, key: name ?? '', value: nextValue, formattedValue }], - fields: fields.map((field) => { - let key: string = name ?? ''; - if (type === 'composite' && name !== null) { - const { 1: fieldName } = field.key.split('.'); - key = `${name}.${fieldName}`; - } - return { - ...field, - key, - }; - }), + fields: updatedFields, }; }); }, [name, type]); diff --git a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx index 05afb1772d0ca..578ed785df86c 100644 --- a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx @@ -42,7 +42,11 @@ export const getFieldDeleteModalOpener = ({ }: Dependencies) => (options: OpenFieldDeleteModalOptions): CloseEditor => { if (typeof options.fieldName === 'string') { const fieldToDelete = options.ctx.indexPattern.getFieldByName(options.fieldName); - if (fieldToDelete?.runtimeField?.parent !== undefined) { + const parent = fieldToDelete?.runtimeField?.parent; + const doesBelongToCompositeField = + parent === undefined ? false : options.ctx.indexPattern.getRuntimeComposite(parent) !== null; + + if (doesBelongToCompositeField) { console.log( // eslint-disable-line 'TODO: display a modal to indicate that this field needs to be deleted through its parent.' ); diff --git a/src/plugins/index_pattern_field_editor/public/shared_imports.ts b/src/plugins/index_pattern_field_editor/public/shared_imports.ts index 2827928d1c060..afcd70ef50378 100644 --- a/src/plugins/index_pattern_field_editor/public/shared_imports.ts +++ b/src/plugins/index_pattern_field_editor/public/shared_imports.ts @@ -10,7 +10,13 @@ export { IndexPattern, IndexPatternField, DataPublicPluginStart } from '../../da export { UsageCollectionStart } from '../../usage_collection/public'; -export { RuntimeType, RuntimeField, KBN_FIELD_TYPES, ES_FIELD_TYPES } from '../../data/common'; +export { + RuntimeType, + RuntimeField, + KBN_FIELD_TYPES, + ES_FIELD_TYPES, + EnhancedRuntimeField, +} from '../../data/common'; export { createKibanaReactContext, toMountPoint, CodeEditor } from '../../kibana_react/public'; diff --git a/src/plugins/index_pattern_field_editor/public/types.ts b/src/plugins/index_pattern_field_editor/public/types.ts index 2cc718b3a9178..e465f5986bbe1 100644 --- a/src/plugins/index_pattern_field_editor/public/types.ts +++ b/src/plugins/index_pattern_field_editor/public/types.ts @@ -8,7 +8,12 @@ import { FunctionComponent } from 'react'; -import { DataPublicPluginStart, RuntimeField, UsageCollectionStart } from './shared_imports'; +import { + DataPublicPluginStart, + RuntimeField, + UsageCollectionStart, + EnhancedRuntimeField, +} from './shared_imports'; import { OpenFieldEditorOptions } from './open_editor'; import { OpenFieldDeleteModalOptions } from './open_delete_modal'; import { FormatEditorServiceSetup, FormatEditorServiceStart } from './service'; @@ -38,11 +43,14 @@ export interface StartPlugins { export type InternalFieldType = 'concrete' | 'runtime'; -export interface Field extends RuntimeField { +export interface Field extends EnhancedRuntimeField { name: string; - customLabel?: string; - popularity?: number; - format?: FieldFormatConfig; +} + +export interface CompositeField extends RuntimeField { + type: 'composite'; + name: string; + subFields: Record; } export interface FieldFormatConfig { From 46d907bc2814040c64337291c1755b7194d94d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 24 Aug 2021 17:22:04 +0100 Subject: [PATCH 14/60] Update comments --- .../index_patterns/index_patterns/index_pattern.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 7c23001cac1c6..ba514195886c3 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -7,6 +7,8 @@ */ import _, { each, reject } from 'lodash'; +import { castEsToKbnFieldTypeName } from '@kbn/field-types'; + import { FieldAttrs, FieldAttrSet, IndexPatternAttributes } from '../..'; import type { EnhancedRuntimeField, @@ -24,7 +26,6 @@ import { flattenHitWrapper } from './flatten_hit'; import { FieldFormatsStartCommon, FieldFormat } from '../../../../field_formats/common'; import { IndexPatternSpec, TypeMeta, SourceFilter, IndexPatternFieldMap } from '../types'; import { SerializedFieldFormat } from '../../../../expressions/common'; -import { castEsToKbnFieldTypeName } from '../../kbn_field_types'; interface IndexPatternDeps { spec?: IndexPatternSpec; @@ -219,7 +220,7 @@ export class IndexPattern implements IIndexPattern { /** * Method to aggregate all the runtime fields which are **not** created * from a parent composite runtime field. - * @returns Runtime fields which are **not** created from a parent composite + * @returns A map of runtime fields */ private getComputedRuntimeFields(): Record { return Object.entries(this.runtimeFieldMap).reduce((acc, [name, field]) => { @@ -243,7 +244,7 @@ export class IndexPattern implements IIndexPattern { /** * This method reads all the runtime composite fields - * and merge into it all the subFields + * and aggregate the subFields * * { * "compositeName": { @@ -555,7 +556,7 @@ export class IndexPattern implements IIndexPattern { } /** - * Add a runtime composite and its subFields to the fields list + * Create a runtime composite and add its subFields to the index pattern fields list * @param name - The runtime composite name * @param runtimeComposite - The runtime composite definition */ @@ -581,6 +582,7 @@ export class IndexPattern implements IIndexPattern { this.runtimeCompositeMap[name] = { name, script, + // We only need to keep a reference of the subFields names subFields: Object.keys(subFields), }; @@ -615,10 +617,10 @@ export class IndexPattern implements IIndexPattern { } const runtimeField: EnhancedRuntimeField = { - type: 'composite', - parent: name, + type: field.type as RuntimeType, customLabel: field.customLabel, popularity: field.count, + parent: name, format: this.getFormatterForFieldNoDefault(field.name)?.toJSON(), }; From 9c589e8c9f18688863441dae3787ebea0042d329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 24 Aug 2021 17:50:16 +0100 Subject: [PATCH 15/60] Update API docs --- ...public.indexpattern.addruntimecomposite.md | 25 +++++++++++++++++++ ...ata-public.indexpattern.addruntimefield.md | 6 ++--- ...a-public.indexpattern.getcomputedfields.md | 8 ++++-- ...public.indexpattern.getruntimecomposite.md | 24 ++++++++++++++++++ ...attern.getruntimecompositewithsubfields.md | 24 ++++++++++++++++++ ...plugin-plugins-data-public.indexpattern.md | 6 ++++- ...lic.indexpattern.removeruntimecomposite.md | 24 ++++++++++++++++++ ...gins-data-public.indexpatternattributes.md | 1 + ...expatternattributes.runtimecompositemap.md | 11 ++++++++ ...in-plugins-data-public.indexpatternspec.md | 1 + ...ic.indexpatternspec.runtimecompositemap.md | 11 ++++++++ .../kibana-plugin-plugins-data-public.md | 1 + ...-plugin-plugins-data-public.runtimetype.md | 11 ++++++++ ...server.indexpattern.addruntimecomposite.md | 25 +++++++++++++++++++ ...ata-server.indexpattern.addruntimefield.md | 6 ++--- ...a-server.indexpattern.getcomputedfields.md | 8 ++++-- ...server.indexpattern.getruntimecomposite.md | 24 ++++++++++++++++++ ...attern.getruntimecompositewithsubfields.md | 24 ++++++++++++++++++ ...plugin-plugins-data-server.indexpattern.md | 6 ++++- ...ver.indexpattern.removeruntimecomposite.md | 24 ++++++++++++++++++ ...gins-data-server.indexpatternattributes.md | 1 + ...expatternattributes.runtimecompositemap.md | 11 ++++++++ src/plugins/data/public/public.api.md | 23 +++++++++++++++-- src/plugins/data/server/server.api.md | 15 +++++++++-- 24 files changed, 304 insertions(+), 16 deletions(-) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.runtimetype.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md new file mode 100644 index 0000000000000..b0e1266ad2997 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [addRuntimeComposite](./kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md) + +## IndexPattern.addRuntimeComposite() method + +Create a runtime composite and add its subFields to the index pattern fields list + +Signature: + +```typescript +addRuntimeComposite(name: string, runtimeComposite: RuntimeCompositeWithSubFields): IndexPatternField[]; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | The runtime composite name | +| runtimeComposite | RuntimeCompositeWithSubFields | The runtime composite definition | + +Returns: + +`IndexPatternField[]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md index 5640395139ba6..1561892e2c47e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md @@ -9,7 +9,7 @@ Add a runtime field - Appended to existing mapped field or a new field is create Signature: ```typescript -addRuntimeField(name: string, runtimeField: RuntimeField): void; +addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): IndexPatternField; ``` ## Parameters @@ -17,9 +17,9 @@ addRuntimeField(name: string, runtimeField: RuntimeField): void; | Parameter | Type | Description | | --- | --- | --- | | name | string | | -| runtimeField | RuntimeField | | +| enhancedRuntimeField | EnhancedRuntimeField | | Returns: -`void` +`IndexPatternField` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getcomputedfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getcomputedfields.md index 37d31a35167df..390a85ed4c288 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getcomputedfields.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getcomputedfields.md @@ -14,7 +14,9 @@ getComputedFields(): { field: any; format: string; }[]; - runtimeFields: Record; + runtimeFields: { + [x: string]: RuntimeField; + }; }; ``` Returns: @@ -26,6 +28,8 @@ getComputedFields(): { field: any; format: string; }[]; - runtimeFields: Record; + runtimeFields: { + [x: string]: RuntimeField; + }; }` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md new file mode 100644 index 0000000000000..1e5db3903e43a --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [getRuntimeComposite](./kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md) + +## IndexPattern.getRuntimeComposite() method + +Returns runtime composite if exists + +Signature: + +```typescript +getRuntimeComposite(name: string): RuntimeComposite | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`RuntimeComposite | null` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md new file mode 100644 index 0000000000000..22f91d6695ce3 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [getRuntimeCompositeWithSubFields](./kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md) + +## IndexPattern.getRuntimeCompositeWithSubFields() method + +Returns runtime composite (if exists) with its subFields + +Signature: + +```typescript +getRuntimeCompositeWithSubFields(name: string): RuntimeCompositeWithSubFields | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`RuntimeCompositeWithSubFields | null` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md index 51ca42fdce70a..1c01effbee127 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md @@ -45,7 +45,8 @@ export declare class IndexPattern implements IIndexPattern | Method | Modifiers | Description | | --- | --- | --- | -| [addRuntimeField(name, runtimeField)](./kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md) | | Add a runtime field - Appended to existing mapped field or a new field is created as appropriate | +| [addRuntimeComposite(name, runtimeComposite)](./kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md) | | Create a runtime composite and add its subFields to the index pattern fields list | +| [addRuntimeField(name, enhancedRuntimeField)](./kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md) | | Add a runtime field - Appended to existing mapped field or a new field is created as appropriate | | [addScriptedField(name, script, fieldType)](./kibana-plugin-plugins-data-public.indexpattern.addscriptedfield.md) | | Add scripted field to field list | | [getAggregationRestrictions()](./kibana-plugin-plugins-data-public.indexpattern.getaggregationrestrictions.md) | | | | [getAsSavedObjectBody()](./kibana-plugin-plugins-data-public.indexpattern.getassavedobjectbody.md) | | Returns index pattern as saved object body for saving | @@ -54,6 +55,8 @@ export declare class IndexPattern implements IIndexPattern | [getFormatterForField(field)](./kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md) | | Provide a field, get its formatter | | [getFormatterForFieldNoDefault(fieldname)](./kibana-plugin-plugins-data-public.indexpattern.getformatterforfieldnodefault.md) | | Get formatter for a given field name. Return undefined if none exists | | [getNonScriptedFields()](./kibana-plugin-plugins-data-public.indexpattern.getnonscriptedfields.md) | | | +| [getRuntimeComposite(name)](./kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md) | | Returns runtime composite if exists | +| [getRuntimeCompositeWithSubFields(name)](./kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md) | | Returns runtime composite (if exists) with its subFields | | [getRuntimeField(name)](./kibana-plugin-plugins-data-public.indexpattern.getruntimefield.md) | | Returns runtime field if exists | | [getScriptedFields()](./kibana-plugin-plugins-data-public.indexpattern.getscriptedfields.md) | | | | [getSourceFiltering()](./kibana-plugin-plugins-data-public.indexpattern.getsourcefiltering.md) | | Get the source filtering configuration for that index. | @@ -61,6 +64,7 @@ export declare class IndexPattern implements IIndexPattern | [hasRuntimeField(name)](./kibana-plugin-plugins-data-public.indexpattern.hasruntimefield.md) | | Checks if runtime field exists | | [isTimeBased()](./kibana-plugin-plugins-data-public.indexpattern.istimebased.md) | | | | [isTimeNanosBased()](./kibana-plugin-plugins-data-public.indexpattern.istimenanosbased.md) | | | +| [removeRuntimeComposite(name)](./kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md) | | Remove a runtime composite with its associated subFields | | [removeRuntimeField(name)](./kibana-plugin-plugins-data-public.indexpattern.removeruntimefield.md) | | Remove a runtime field - removed from mapped field or removed unmapped field as appropriate. Doesn't clear associated field attributes. | | [removeScriptedField(fieldName)](./kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md) | | Remove scripted field from field list | | [replaceAllRuntimeFields(newFields)](./kibana-plugin-plugins-data-public.indexpattern.replaceallruntimefields.md) | | Replaces all existing runtime fields with new fields | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md new file mode 100644 index 0000000000000..3fca980ad5d69 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [removeRuntimeComposite](./kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md) + +## IndexPattern.removeRuntimeComposite() method + +Remove a runtime composite with its associated subFields + +Signature: + +```typescript +removeRuntimeComposite(name: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | Runtime composite name to remove | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md index 41a4d3c55694b..115f86773b629 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md @@ -21,6 +21,7 @@ export interface IndexPatternAttributes | [fieldFormatMap](./kibana-plugin-plugins-data-public.indexpatternattributes.fieldformatmap.md) | string | | | [fields](./kibana-plugin-plugins-data-public.indexpatternattributes.fields.md) | string | | | [intervalName](./kibana-plugin-plugins-data-public.indexpatternattributes.intervalname.md) | string | | +| [runtimeCompositeMap](./kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md) | string | | | [runtimeFieldMap](./kibana-plugin-plugins-data-public.indexpatternattributes.runtimefieldmap.md) | string | | | [sourceFilters](./kibana-plugin-plugins-data-public.indexpatternattributes.sourcefilters.md) | string | | | [timeFieldName](./kibana-plugin-plugins-data-public.indexpatternattributes.timefieldname.md) | string | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md new file mode 100644 index 0000000000000..40e939013f157 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternAttributes](./kibana-plugin-plugins-data-public.indexpatternattributes.md) > [runtimeCompositeMap](./kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md) + +## IndexPatternAttributes.runtimeCompositeMap property + +Signature: + +```typescript +runtimeCompositeMap?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md index ae514e3fc6a8a..a2a8482af2c61 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md @@ -22,6 +22,7 @@ export interface IndexPatternSpec | [fields](./kibana-plugin-plugins-data-public.indexpatternspec.fields.md) | IndexPatternFieldMap | | | [id](./kibana-plugin-plugins-data-public.indexpatternspec.id.md) | string | saved object id | | [intervalName](./kibana-plugin-plugins-data-public.indexpatternspec.intervalname.md) | string | | +| [runtimeCompositeMap](./kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md) | Record<string, RuntimeComposite> | | | [runtimeFieldMap](./kibana-plugin-plugins-data-public.indexpatternspec.runtimefieldmap.md) | Record<string, RuntimeField> | | | [sourceFilters](./kibana-plugin-plugins-data-public.indexpatternspec.sourcefilters.md) | SourceFilter[] | | | [timeFieldName](./kibana-plugin-plugins-data-public.indexpatternspec.timefieldname.md) | string | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md new file mode 100644 index 0000000000000..4667de1abdd9b --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) > [runtimeCompositeMap](./kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md) + +## IndexPatternSpec.runtimeCompositeMap property + +Signature: + +```typescript +runtimeCompositeMap?: Record; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index 7548aa62eb313..0942f3eb15cdb 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -176,6 +176,7 @@ | [RangeFilterMeta](./kibana-plugin-plugins-data-public.rangefiltermeta.md) | | | [RangeFilterParams](./kibana-plugin-plugins-data-public.rangefilterparams.md) | | | [RefreshInterval](./kibana-plugin-plugins-data-public.refreshinterval.md) | | +| [RuntimeType](./kibana-plugin-plugins-data-public.runtimetype.md) | | | [SavedQueryTimeFilter](./kibana-plugin-plugins-data-public.savedquerytimefilter.md) | | | [SearchBarProps](./kibana-plugin-plugins-data-public.searchbarprops.md) | | | [StatefulSearchBarProps](./kibana-plugin-plugins-data-public.statefulsearchbarprops.md) | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.runtimetype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.runtimetype.md new file mode 100644 index 0000000000000..35d8d42ba7ab3 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.runtimetype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RuntimeType](./kibana-plugin-plugins-data-public.runtimetype.md) + +## RuntimeType type + +Signature: + +```typescript +export declare type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md new file mode 100644 index 0000000000000..a5dfa4fb50ba4 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [addRuntimeComposite](./kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md) + +## IndexPattern.addRuntimeComposite() method + +Create a runtime composite and add its subFields to the index pattern fields list + +Signature: + +```typescript +addRuntimeComposite(name: string, runtimeComposite: RuntimeCompositeWithSubFields): IndexPatternField[]; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | The runtime composite name | +| runtimeComposite | RuntimeCompositeWithSubFields | The runtime composite definition | + +Returns: + +`IndexPatternField[]` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md index ebd7f46d3598e..dcb1b02b653ba 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md @@ -9,7 +9,7 @@ Add a runtime field - Appended to existing mapped field or a new field is create Signature: ```typescript -addRuntimeField(name: string, runtimeField: RuntimeField): void; +addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): IndexPatternField; ``` ## Parameters @@ -17,9 +17,9 @@ addRuntimeField(name: string, runtimeField: RuntimeField): void; | Parameter | Type | Description | | --- | --- | --- | | name | string | | -| runtimeField | RuntimeField | | +| enhancedRuntimeField | EnhancedRuntimeField | | Returns: -`void` +`IndexPatternField` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md index 0030adf1261e4..580401a955d24 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md @@ -14,7 +14,9 @@ getComputedFields(): { field: any; format: string; }[]; - runtimeFields: Record; + runtimeFields: { + [x: string]: RuntimeField; + }; }; ``` Returns: @@ -26,6 +28,8 @@ getComputedFields(): { field: any; format: string; }[]; - runtimeFields: Record; + runtimeFields: { + [x: string]: RuntimeField; + }; }` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md new file mode 100644 index 0000000000000..7594ef8cdb804 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getRuntimeComposite](./kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md) + +## IndexPattern.getRuntimeComposite() method + +Returns runtime composite if exists + +Signature: + +```typescript +getRuntimeComposite(name: string): RuntimeComposite | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`RuntimeComposite | null` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md new file mode 100644 index 0000000000000..8b6cda97ebb50 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getRuntimeCompositeWithSubFields](./kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md) + +## IndexPattern.getRuntimeCompositeWithSubFields() method + +Returns runtime composite (if exists) with its subFields + +Signature: + +```typescript +getRuntimeCompositeWithSubFields(name: string): RuntimeCompositeWithSubFields | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`RuntimeCompositeWithSubFields | null` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md index 27b8a31a2582b..b20765ce5d46c 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md @@ -45,7 +45,8 @@ export declare class IndexPattern implements IIndexPattern | Method | Modifiers | Description | | --- | --- | --- | -| [addRuntimeField(name, runtimeField)](./kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md) | | Add a runtime field - Appended to existing mapped field or a new field is created as appropriate | +| [addRuntimeComposite(name, runtimeComposite)](./kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md) | | Create a runtime composite and add its subFields to the index pattern fields list | +| [addRuntimeField(name, enhancedRuntimeField)](./kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md) | | Add a runtime field - Appended to existing mapped field or a new field is created as appropriate | | [addScriptedField(name, script, fieldType)](./kibana-plugin-plugins-data-server.indexpattern.addscriptedfield.md) | | Add scripted field to field list | | [getAggregationRestrictions()](./kibana-plugin-plugins-data-server.indexpattern.getaggregationrestrictions.md) | | | | [getAsSavedObjectBody()](./kibana-plugin-plugins-data-server.indexpattern.getassavedobjectbody.md) | | Returns index pattern as saved object body for saving | @@ -54,6 +55,8 @@ export declare class IndexPattern implements IIndexPattern | [getFormatterForField(field)](./kibana-plugin-plugins-data-server.indexpattern.getformatterforfield.md) | | Provide a field, get its formatter | | [getFormatterForFieldNoDefault(fieldname)](./kibana-plugin-plugins-data-server.indexpattern.getformatterforfieldnodefault.md) | | Get formatter for a given field name. Return undefined if none exists | | [getNonScriptedFields()](./kibana-plugin-plugins-data-server.indexpattern.getnonscriptedfields.md) | | | +| [getRuntimeComposite(name)](./kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md) | | Returns runtime composite if exists | +| [getRuntimeCompositeWithSubFields(name)](./kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md) | | Returns runtime composite (if exists) with its subFields | | [getRuntimeField(name)](./kibana-plugin-plugins-data-server.indexpattern.getruntimefield.md) | | Returns runtime field if exists | | [getScriptedFields()](./kibana-plugin-plugins-data-server.indexpattern.getscriptedfields.md) | | | | [getSourceFiltering()](./kibana-plugin-plugins-data-server.indexpattern.getsourcefiltering.md) | | Get the source filtering configuration for that index. | @@ -61,6 +64,7 @@ export declare class IndexPattern implements IIndexPattern | [hasRuntimeField(name)](./kibana-plugin-plugins-data-server.indexpattern.hasruntimefield.md) | | Checks if runtime field exists | | [isTimeBased()](./kibana-plugin-plugins-data-server.indexpattern.istimebased.md) | | | | [isTimeNanosBased()](./kibana-plugin-plugins-data-server.indexpattern.istimenanosbased.md) | | | +| [removeRuntimeComposite(name)](./kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md) | | Remove a runtime composite with its associated subFields | | [removeRuntimeField(name)](./kibana-plugin-plugins-data-server.indexpattern.removeruntimefield.md) | | Remove a runtime field - removed from mapped field or removed unmapped field as appropriate. Doesn't clear associated field attributes. | | [removeScriptedField(fieldName)](./kibana-plugin-plugins-data-server.indexpattern.removescriptedfield.md) | | Remove scripted field from field list | | [replaceAllRuntimeFields(newFields)](./kibana-plugin-plugins-data-server.indexpattern.replaceallruntimefields.md) | | Replaces all existing runtime fields with new fields | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md new file mode 100644 index 0000000000000..9cf8385152057 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [removeRuntimeComposite](./kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md) + +## IndexPattern.removeRuntimeComposite() method + +Remove a runtime composite with its associated subFields + +Signature: + +```typescript +removeRuntimeComposite(name: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | Runtime composite name to remove | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md index 20af97ecc8761..4520383338182 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md @@ -21,6 +21,7 @@ export interface IndexPatternAttributes | [fieldFormatMap](./kibana-plugin-plugins-data-server.indexpatternattributes.fieldformatmap.md) | string | | | [fields](./kibana-plugin-plugins-data-server.indexpatternattributes.fields.md) | string | | | [intervalName](./kibana-plugin-plugins-data-server.indexpatternattributes.intervalname.md) | string | | +| [runtimeCompositeMap](./kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md) | string | | | [runtimeFieldMap](./kibana-plugin-plugins-data-server.indexpatternattributes.runtimefieldmap.md) | string | | | [sourceFilters](./kibana-plugin-plugins-data-server.indexpatternattributes.sourcefilters.md) | string | | | [timeFieldName](./kibana-plugin-plugins-data-server.indexpatternattributes.timefieldname.md) | string | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md new file mode 100644 index 0000000000000..6ecf4854873ef --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) > [runtimeCompositeMap](./kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md) + +## IndexPatternAttributes.runtimeCompositeMap property + +Signature: + +```typescript +runtimeCompositeMap?: string; +``` diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index d5a39e3108325..bf37adfa991e8 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1167,7 +1167,10 @@ export const INDEX_PATTERN_SAVED_OBJECT_TYPE = "index-pattern"; export class IndexPattern implements IIndexPattern { // Warning: (ae-forgotten-export) The symbol "IndexPatternDeps" needs to be exported by the entry point index.d.ts constructor({ spec, fieldFormats, shortDotsEnable, metaFields, }: IndexPatternDeps); - addRuntimeField(name: string, runtimeField: RuntimeField): void; + // Warning: (ae-forgotten-export) The symbol "RuntimeCompositeWithSubFields" needs to be exported by the entry point index.d.ts + addRuntimeComposite(name: string, runtimeComposite: RuntimeCompositeWithSubFields): IndexPatternField[]; + // Warning: (ae-forgotten-export) The symbol "EnhancedRuntimeField" needs to be exported by the entry point index.d.ts + addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): IndexPatternField; // @deprecated addScriptedField(name: string, script: string, fieldType?: string): Promise; readonly allowNoIndex: boolean; @@ -1206,7 +1209,9 @@ export class IndexPattern implements IIndexPattern { field: any; format: string; }[]; - runtimeFields: Record; + runtimeFields: { + [x: string]: RuntimeField; + }; }; // (undocumented) getFieldAttrs: () => { @@ -1229,6 +1234,9 @@ export class IndexPattern implements IIndexPattern { typeMeta?: string | undefined; type?: string | undefined; }; + // Warning: (ae-forgotten-export) The symbol "RuntimeComposite" needs to be exported by the entry point index.d.ts + getRuntimeComposite(name: string): RuntimeComposite | null; + getRuntimeCompositeWithSubFields(name: string): RuntimeCompositeWithSubFields | null; getRuntimeField(name: string): RuntimeField | null; // @deprecated (undocumented) getScriptedFields(): IndexPatternField[]; @@ -1248,6 +1256,7 @@ export class IndexPattern implements IIndexPattern { isTimeNanosBased(): boolean; // (undocumented) metaFields: string[]; + removeRuntimeComposite(name: string): void; removeRuntimeField(name: string): void; // @deprecated removeScriptedField(fieldName: string): void; @@ -1289,6 +1298,8 @@ export interface IndexPatternAttributes { // (undocumented) intervalName?: string; // (undocumented) + runtimeCompositeMap?: string; + // (undocumented) runtimeFieldMap?: string; // (undocumented) sourceFilters?: string; @@ -1440,6 +1451,8 @@ export interface IndexPatternSpec { // @deprecated (undocumented) intervalName?: string; // (undocumented) + runtimeCompositeMap?: Record; + // (undocumented) runtimeFieldMap?: Record; // (undocumented) sourceFilters?: SourceFilter[]; @@ -1960,6 +1973,12 @@ export type RefreshInterval = { value: number; }; +// Warning: (ae-forgotten-export) The symbol "RUNTIME_FIELD_TYPES" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "RuntimeType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; + // Warning: (ae-missing-release-tag) "SavedQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index f994db960669f..04611a0ff9d8f 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -269,7 +269,10 @@ export const INDEX_PATTERN_SAVED_OBJECT_TYPE = "index-pattern"; export class IndexPattern implements IIndexPattern { // Warning: (ae-forgotten-export) The symbol "IndexPatternDeps" needs to be exported by the entry point index.d.ts constructor({ spec, fieldFormats, shortDotsEnable, metaFields, }: IndexPatternDeps); - addRuntimeField(name: string, runtimeField: RuntimeField): void; + // Warning: (ae-forgotten-export) The symbol "RuntimeCompositeWithSubFields" needs to be exported by the entry point index.d.ts + addRuntimeComposite(name: string, runtimeComposite: RuntimeCompositeWithSubFields): IndexPatternField[]; + // Warning: (ae-forgotten-export) The symbol "EnhancedRuntimeField" needs to be exported by the entry point index.d.ts + addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): IndexPatternField; // @deprecated addScriptedField(name: string, script: string, fieldType?: string): Promise; readonly allowNoIndex: boolean; @@ -310,7 +313,9 @@ export class IndexPattern implements IIndexPattern { field: any; format: string; }[]; - runtimeFields: Record; + runtimeFields: { + [x: string]: RuntimeField; + }; }; // (undocumented) getFieldAttrs: () => { @@ -334,6 +339,9 @@ export class IndexPattern implements IIndexPattern { typeMeta?: string | undefined; type?: string | undefined; }; + // Warning: (ae-forgotten-export) The symbol "RuntimeComposite" needs to be exported by the entry point index.d.ts + getRuntimeComposite(name: string): RuntimeComposite | null; + getRuntimeCompositeWithSubFields(name: string): RuntimeCompositeWithSubFields | null; getRuntimeField(name: string): RuntimeField | null; // @deprecated (undocumented) getScriptedFields(): IndexPatternField[]; @@ -353,6 +361,7 @@ export class IndexPattern implements IIndexPattern { isTimeNanosBased(): boolean; // (undocumented) metaFields: string[]; + removeRuntimeComposite(name: string): void; removeRuntimeField(name: string): void; // @deprecated removeScriptedField(fieldName: string): void; @@ -398,6 +407,8 @@ export interface IndexPatternAttributes { // (undocumented) intervalName?: string; // (undocumented) + runtimeCompositeMap?: string; + // (undocumented) runtimeFieldMap?: string; // (undocumented) sourceFilters?: string; From b10c61abc6eeba37119efd160cf0316753aba3d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 24 Aug 2021 17:50:35 +0100 Subject: [PATCH 16/60] Fix FieldFormatConfig type --- src/plugins/index_pattern_field_editor/public/types.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/plugins/index_pattern_field_editor/public/types.ts b/src/plugins/index_pattern_field_editor/public/types.ts index e465f5986bbe1..3913cb183ba0f 100644 --- a/src/plugins/index_pattern_field_editor/public/types.ts +++ b/src/plugins/index_pattern_field_editor/public/types.ts @@ -53,10 +53,7 @@ export interface CompositeField extends RuntimeField { subFields: Record; } -export interface FieldFormatConfig { - id: string; - params?: { [key: string]: any }; -} +export type FieldFormatConfig = EnhancedRuntimeField['format']; export type CloseEditor = () => void; From cc14ed77439093a2b4bce14e36ca2b25af62fb30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Wed, 25 Aug 2021 12:49:53 +0100 Subject: [PATCH 17/60] Update API docs --- ...na-plugin-plugins-data-public.esfilters.md | 34 ++++----- ...bana-plugin-plugins-data-public.gettime.md | 4 +- ...na-plugin-plugins-data-server.esfilters.md | 12 ++-- ...bana-plugin-plugins-data-server.gettime.md | 4 +- src/plugins/data/public/public.api.md | 72 +++++++++---------- src/plugins/data/server/server.api.md | 22 +++--- 6 files changed, 66 insertions(+), 82 deletions(-) diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md index 1b61d9a253026..2500ed9b2bc05 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md @@ -20,28 +20,24 @@ esFilters: { FILTERS: typeof import("@kbn/es-query").FILTERS; FilterStateStore: typeof FilterStateStore; buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("@kbn/es-query").Filter; - buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: (string | number | boolean)[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; + buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: string[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; buildExistsFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").ExistsFilter; - buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").ScriptedPhraseFilter; - buildQueryFilter: (query: (Record & { - query_string?: { - query: string; - } | undefined; - }) | undefined, index: string, alias: string) => import("@kbn/es-query").QueryStringFilter; - buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter; - isPhraseFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").PhraseFilter; - isExistsFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").ExistsFilter; - isPhrasesFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").PhrasesFilter; - isRangeFilter: (filter?: import("@kbn/es-query").Filter | undefined) => filter is import("@kbn/es-query").RangeFilter; - isMatchAllFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").MatchAllFilter; - isMissingFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").MissingFilter; - isQueryStringFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").QueryStringFilter; + buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter; + buildQueryFilter: (query: any, index: string, alias: string) => import("@kbn/es-query/target_types/filters/build_filters").QueryStringFilter; + buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter; + isPhraseFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").PhraseFilter; + isExistsFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").ExistsFilter; + isPhrasesFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").PhrasesFilter; + isRangeFilter: (filter?: import("@kbn/es-query").ExistsFilter | import("@kbn/es-query").PhrasesFilter | import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").MatchAllFilter | import("@kbn/es-query").MissingFilter | import("@kbn/es-query").RangeFilter | undefined) => filter is import("@kbn/es-query").RangeFilter; + isMatchAllFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").MatchAllFilter; + isMissingFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").MissingFilter; + isQueryStringFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query/target_types/filters/build_filters").QueryStringFilter; isFilterPinned: (filter: import("@kbn/es-query").Filter) => boolean | undefined; toggleFilterNegated: (filter: import("@kbn/es-query").Filter) => { meta: { negate: boolean; - alias?: string | null | undefined; - disabled?: boolean | undefined; + alias: string | null; + disabled: boolean; controlledBy?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; @@ -53,11 +49,11 @@ esFilters: { $state?: { store: FilterStateStore; } | undefined; - query?: Record | undefined; + query?: any; }; disableFilter: (filter: import("@kbn/es-query").Filter) => import("@kbn/es-query").Filter; getPhraseFilterField: (filter: import("@kbn/es-query").PhraseFilter) => string; - getPhraseFilterValue: (filter: import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").ScriptedPhraseFilter) => string | number | boolean; + getPhraseFilterValue: (filter: import("@kbn/es-query").PhraseFilter) => string | number | boolean; getDisplayValueFromFilter: typeof getDisplayValueFromFilter; compareFilters: (first: import("@kbn/es-query").Filter | import("@kbn/es-query").Filter[], second: import("@kbn/es-query").Filter | import("@kbn/es-query").Filter[], comparatorOptions?: import("@kbn/es-query").FilterCompareOptions | undefined) => boolean; COMPARE_ALL_OPTIONS: import("@kbn/es-query").FilterCompareOptions; diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md index 5e208a9bcf0a9..7fd1914d1a4a5 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md @@ -10,7 +10,7 @@ export declare function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, options?: { forceNow?: Date; fieldName?: string; -}): import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter | undefined; +}): import("@kbn/es-query").RangeFilter | undefined; ``` ## Parameters @@ -23,5 +23,5 @@ export declare function getTime(indexPattern: IIndexPattern | undefined, timeRan Returns: -`import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter | undefined` +`import("@kbn/es-query").RangeFilter | undefined` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md index fa95ea72035dd..b37d0555194fc 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md @@ -8,18 +8,14 @@ ```typescript esFilters: { - buildQueryFilter: (query: (Record & { - query_string?: { - query: string; - } | undefined; - }) | undefined, index: string, alias: string) => import("@kbn/es-query").QueryStringFilter; + buildQueryFilter: (query: any, index: string, alias: string) => import("@kbn/es-query/target_types/filters/build_filters").QueryStringFilter; buildCustomFilter: typeof import("@kbn/es-query").buildCustomFilter; buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("@kbn/es-query").Filter; buildExistsFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").ExistsFilter; buildFilter: typeof import("@kbn/es-query").buildFilter; - buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").ScriptedPhraseFilter; - buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: (string | number | boolean)[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; - buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter; + buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter; + buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: string[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; + buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter; isFilterDisabled: (filter: import("@kbn/es-query").Filter) => boolean; } ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.gettime.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.gettime.md index 168be5db779a2..7f2267aff7049 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.gettime.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.gettime.md @@ -10,7 +10,7 @@ export declare function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, options?: { forceNow?: Date; fieldName?: string; -}): import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter | undefined; +}): import("@kbn/es-query").RangeFilter | undefined; ``` ## Parameters @@ -23,5 +23,5 @@ export declare function getTime(indexPattern: IIndexPattern | undefined, timeRan Returns: -`import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter | undefined` +`import("@kbn/es-query").RangeFilter | undefined` diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index bf37adfa991e8..3f9b88a4a64c3 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -738,28 +738,24 @@ export const esFilters: { FILTERS: typeof import("@kbn/es-query").FILTERS; FilterStateStore: typeof FilterStateStore; buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("@kbn/es-query").Filter; - buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: (string | number | boolean)[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; + buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: string[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; buildExistsFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").ExistsFilter; - buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").ScriptedPhraseFilter; - buildQueryFilter: (query: (Record & { - query_string?: { - query: string; - } | undefined; - }) | undefined, index: string, alias: string) => import("@kbn/es-query").QueryStringFilter; - buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter; - isPhraseFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").PhraseFilter; - isExistsFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").ExistsFilter; - isPhrasesFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").PhrasesFilter; - isRangeFilter: (filter?: import("@kbn/es-query").Filter | undefined) => filter is import("@kbn/es-query").RangeFilter; - isMatchAllFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").MatchAllFilter; - isMissingFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").MissingFilter; - isQueryStringFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").QueryStringFilter; + buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter; + buildQueryFilter: (query: any, index: string, alias: string) => import("@kbn/es-query/target_types/filters/build_filters").QueryStringFilter; + buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter; + isPhraseFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").PhraseFilter; + isExistsFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").ExistsFilter; + isPhrasesFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").PhrasesFilter; + isRangeFilter: (filter?: import("@kbn/es-query").ExistsFilter | import("@kbn/es-query").PhrasesFilter | import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").MatchAllFilter | import("@kbn/es-query").MissingFilter | import("@kbn/es-query").RangeFilter | undefined) => filter is import("@kbn/es-query").RangeFilter; + isMatchAllFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").MatchAllFilter; + isMissingFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").MissingFilter; + isQueryStringFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query/target_types/filters/build_filters").QueryStringFilter; isFilterPinned: (filter: import("@kbn/es-query").Filter) => boolean | undefined; toggleFilterNegated: (filter: import("@kbn/es-query").Filter) => { meta: { negate: boolean; - alias?: string | null | undefined; - disabled?: boolean | undefined; + alias: string | null; + disabled: boolean; controlledBy?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; @@ -771,11 +767,11 @@ export const esFilters: { $state?: { store: FilterStateStore; } | undefined; - query?: Record | undefined; + query?: any; }; disableFilter: (filter: import("@kbn/es-query").Filter) => import("@kbn/es-query").Filter; getPhraseFilterField: (filter: import("@kbn/es-query").PhraseFilter) => string; - getPhraseFilterValue: (filter: import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").ScriptedPhraseFilter) => string | number | boolean; + getPhraseFilterValue: (filter: import("@kbn/es-query").PhraseFilter) => string | number | boolean; getDisplayValueFromFilter: typeof getDisplayValueFromFilter; compareFilters: (first: import("@kbn/es-query").Filter | import("@kbn/es-query").Filter[], second: import("@kbn/es-query").Filter | import("@kbn/es-query").Filter[], comparatorOptions?: import("@kbn/es-query").FilterCompareOptions | undefined) => boolean; COMPARE_ALL_OPTIONS: import("@kbn/es-query").FilterCompareOptions; @@ -1005,7 +1001,7 @@ export function getSearchParamsFromRequest(searchRequest: SearchRequest, depende export function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, options?: { forceNow?: Date; fieldName?: string; -}): import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter | undefined; +}): import("@kbn/es-query").RangeFilter | undefined; // Warning: (ae-missing-release-tag) "IAggConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2359,10 +2355,10 @@ export interface WaitUntilNextSessionCompletesOptions { // Warnings were encountered during analysis: // -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:52:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:66:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:139:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:170:7 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:59:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:73:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:148:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:172:20 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts // src/plugins/data/common/search/aggs/types.ts:128:51 - (ae-forgotten-export) The symbol "AggTypesRegistryStart" needs to be exported by the entry point index.d.ts // src/plugins/data/common/search/search_source/fetch/get_search_params.ts:35:19 - (ae-forgotten-export) The symbol "GetConfigFn" needs to be exported by the entry point index.d.ts // src/plugins/data/public/deprecated.ts:98:23 - (ae-forgotten-export) The symbol "changeTimeFilter" needs to be exported by the entry point index.d.ts @@ -2373,20 +2369,20 @@ export interface WaitUntilNextSessionCompletesOptions { // src/plugins/data/public/index.ts:53:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:53:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:53:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:211:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:211:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:211:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:213:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:214:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:223:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:224:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:225:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:226:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:230:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:231:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:234:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:235:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:238:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:212:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:212:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:212:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:214:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:215:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:224:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:225:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:226:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:227:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:231:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:232:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:235:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:236:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:239:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/search/session/session_service.ts:62:5 - (ae-forgotten-export) The symbol "UrlGeneratorStateMapping" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 04611a0ff9d8f..85994d0f172b5 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -108,18 +108,14 @@ export const ES_SEARCH_STRATEGY = "es"; // // @public (undocumented) export const esFilters: { - buildQueryFilter: (query: (Record & { - query_string?: { - query: string; - } | undefined; - }) | undefined, index: string, alias: string) => import("@kbn/es-query").QueryStringFilter; + buildQueryFilter: (query: any, index: string, alias: string) => import("@kbn/es-query/target_types/filters/build_filters").QueryStringFilter; buildCustomFilter: typeof import("@kbn/es-query").buildCustomFilter; buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("@kbn/es-query").Filter; buildExistsFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").ExistsFilter; buildFilter: typeof import("@kbn/es-query").buildFilter; - buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").ScriptedPhraseFilter; - buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: (string | number | boolean)[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; - buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter; + buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter; + buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: string[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; + buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter; isFilterDisabled: (filter: import("@kbn/es-query").Filter) => boolean; }; @@ -203,7 +199,7 @@ export function getEsQueryConfig(config: KibanaConfig): EsQueryConfig_2; export function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, options?: { forceNow?: Date; fieldName?: string; -}): import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter | undefined; +}): import("@kbn/es-query").RangeFilter | undefined; // Warning: (ae-forgotten-export) The symbol "IKibanaSearchRequest" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ISearchRequestParams" needs to be exported by the entry point index.d.ts @@ -872,10 +868,10 @@ export const UI_SETTINGS: { // Warnings were encountered during analysis: // -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:52:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:66:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:139:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:170:7 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:59:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:73:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:148:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:172:20 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:21:23 - (ae-forgotten-export) The symbol "datatableToCSV" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:97:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:98:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts From 85296a5e2e0709366b82770ae105046fae570978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Thu, 26 Aug 2021 11:26:16 +0100 Subject: [PATCH 18/60] Update indexPatternSpecSchema on server --- .../server/index_patterns/routes/create_index_pattern.ts | 2 ++ .../data/server/index_patterns/routes/util/schemas.ts | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts b/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts index 7049903f84e8c..06dd09a641ede 100644 --- a/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts +++ b/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts @@ -12,6 +12,7 @@ import { handleErrors } from './util/handle_errors'; import { fieldSpecSchema, runtimeFieldSpecSchema, + runtimeCompositeSpecSchema, serializedFieldFormatSchema, } from './util/schemas'; import { IRouter, StartServicesAccessor } from '../../../../../core/server'; @@ -44,6 +45,7 @@ const indexPatternSpecSchema = schema.object({ ), allowNoIndex: schema.maybe(schema.boolean()), runtimeFieldMap: schema.maybe(schema.recordOf(schema.string(), runtimeFieldSpecSchema)), + runtimeCompositeMap: schema.maybe(schema.recordOf(schema.string(), runtimeCompositeSpecSchema)), }); export const registerCreateIndexPatternRoute = ( diff --git a/src/plugins/data/server/index_patterns/routes/util/schemas.ts b/src/plugins/data/server/index_patterns/routes/util/schemas.ts index 79ee1ffa1ab97..7fc1030e4eaee 100644 --- a/src/plugins/data/server/index_patterns/routes/util/schemas.ts +++ b/src/plugins/data/server/index_patterns/routes/util/schemas.ts @@ -74,3 +74,10 @@ export const runtimeFieldSpec = { ), }; export const runtimeFieldSpecSchema = schema.object(runtimeFieldSpec); + +export const runtimeCompositeSpec = { + name: schema.string(), + script: runtimeFieldSpec.script, + fields: schema.arrayOf(schema.string()), +}; +export const runtimeCompositeSpecSchema = schema.object(runtimeCompositeSpec); From a4e1113cc16ea752c038011eb59182f4fdec1f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Thu, 26 Aug 2021 12:15:27 +0100 Subject: [PATCH 19/60] Fix component integration tests --- .../components/preview/field_preview.tsx | 29 ++++++++++--------- .../preview/field_preview_context.tsx | 13 +++++++-- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview.tsx b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview.tsx index 09bacf2a46096..7fc8dae1f025c 100644 --- a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview.tsx @@ -27,6 +27,7 @@ export const FieldPreview = () => { params: { value: { name, script, format }, }, + isLoadingPreview, fields, error, reset, @@ -34,15 +35,15 @@ export const FieldPreview = () => { // To show the preview we at least need a name to be defined, the script or the format // and an first response from the _execute API - const isEmptyPromptVisible = - name === null && script === null && format === null - ? true - : // If we have some result from the _execute API call don't show the empty prompt - error !== null || fields.length > 0 - ? false - : name === null && format === null - ? true - : false; + let isEmptyPromptVisible = false; + const noParamDefined = name === null && script === null && format === null; + const haveResultFromPreview = error !== null || fields.length > 0; + + if (noParamDefined) { + isEmptyPromptVisible = true; + } else if (!haveResultFromPreview && !isLoadingPreview && name === null && format === null) { + isEmptyPromptVisible = true; + } const onFieldListResize = useCallback(({ height }: { height: number }) => { setFieldListHeight(height); @@ -53,13 +54,13 @@ export const FieldPreview = () => { return null; } - const [field] = fields; - return (
    -
  • - -
  • + {fields.map((field, i) => ( +
  • + +
  • + ))}
); }; diff --git a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx index d91cc8e25bbe8..2c74f4899f159 100644 --- a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx @@ -601,7 +601,7 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { setPreviewResponse((prev) => { const { fields } = prev; - const updatedFields = fields.map((field) => { + let updatedFields: Context['fields'] = fields.map((field) => { let key: string = name ?? ''; if (type === 'composite') { @@ -615,6 +615,14 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { }; }); + // If the user has entered a name but not yet any script we will display + // the field in the preview with just the name (and a "-" for the value) + if (updatedFields.length === 0 && name !== null) { + updatedFields = [ + { key: name, value: undefined, formattedValue: defaultValueFormatter(undefined) }, + ]; + } + return { ...prev, fields: updatedFields, @@ -631,11 +639,10 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { return { ...prev, - // fields: [{ ...field, key: name ?? '', value: nextValue, formattedValue }], fields: fields.map((field) => { const nextValue = script === null && Boolean(document) - ? get(document, name ?? '') // When there is no script we read the value from _source + ? get(document, name ?? '') // When there is no script we try to read the value from _source : field?.value; const formattedValue = valueFormatter(nextValue); From 183db295673fb1445db0f8c916fafa2cc75b2f4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Thu, 26 Aug 2021 13:42:04 +0100 Subject: [PATCH 20/60] Update API docs --- ...na-plugin-plugins-data-public.esfilters.md | 34 ++++++++++-------- ...bana-plugin-plugins-data-public.gettime.md | 4 +-- ...na-plugin-plugins-data-server.esfilters.md | 12 ++++--- ...bana-plugin-plugins-data-server.gettime.md | 4 +-- src/plugins/data/public/public.api.md | 36 ++++++++++--------- src/plugins/data/server/server.api.md | 14 +++++--- 6 files changed, 60 insertions(+), 44 deletions(-) diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md index 2500ed9b2bc05..1b61d9a253026 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md @@ -20,24 +20,28 @@ esFilters: { FILTERS: typeof import("@kbn/es-query").FILTERS; FilterStateStore: typeof FilterStateStore; buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("@kbn/es-query").Filter; - buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: string[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; + buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: (string | number | boolean)[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; buildExistsFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").ExistsFilter; - buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter; - buildQueryFilter: (query: any, index: string, alias: string) => import("@kbn/es-query/target_types/filters/build_filters").QueryStringFilter; - buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter; - isPhraseFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").PhraseFilter; - isExistsFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").ExistsFilter; - isPhrasesFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").PhrasesFilter; - isRangeFilter: (filter?: import("@kbn/es-query").ExistsFilter | import("@kbn/es-query").PhrasesFilter | import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").MatchAllFilter | import("@kbn/es-query").MissingFilter | import("@kbn/es-query").RangeFilter | undefined) => filter is import("@kbn/es-query").RangeFilter; - isMatchAllFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").MatchAllFilter; - isMissingFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").MissingFilter; - isQueryStringFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query/target_types/filters/build_filters").QueryStringFilter; + buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").ScriptedPhraseFilter; + buildQueryFilter: (query: (Record & { + query_string?: { + query: string; + } | undefined; + }) | undefined, index: string, alias: string) => import("@kbn/es-query").QueryStringFilter; + buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter; + isPhraseFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").PhraseFilter; + isExistsFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").ExistsFilter; + isPhrasesFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").PhrasesFilter; + isRangeFilter: (filter?: import("@kbn/es-query").Filter | undefined) => filter is import("@kbn/es-query").RangeFilter; + isMatchAllFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").MatchAllFilter; + isMissingFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").MissingFilter; + isQueryStringFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").QueryStringFilter; isFilterPinned: (filter: import("@kbn/es-query").Filter) => boolean | undefined; toggleFilterNegated: (filter: import("@kbn/es-query").Filter) => { meta: { negate: boolean; - alias: string | null; - disabled: boolean; + alias?: string | null | undefined; + disabled?: boolean | undefined; controlledBy?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; @@ -49,11 +53,11 @@ esFilters: { $state?: { store: FilterStateStore; } | undefined; - query?: any; + query?: Record | undefined; }; disableFilter: (filter: import("@kbn/es-query").Filter) => import("@kbn/es-query").Filter; getPhraseFilterField: (filter: import("@kbn/es-query").PhraseFilter) => string; - getPhraseFilterValue: (filter: import("@kbn/es-query").PhraseFilter) => string | number | boolean; + getPhraseFilterValue: (filter: import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").ScriptedPhraseFilter) => string | number | boolean; getDisplayValueFromFilter: typeof getDisplayValueFromFilter; compareFilters: (first: import("@kbn/es-query").Filter | import("@kbn/es-query").Filter[], second: import("@kbn/es-query").Filter | import("@kbn/es-query").Filter[], comparatorOptions?: import("@kbn/es-query").FilterCompareOptions | undefined) => boolean; COMPARE_ALL_OPTIONS: import("@kbn/es-query").FilterCompareOptions; diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md index 7fd1914d1a4a5..5e208a9bcf0a9 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md @@ -10,7 +10,7 @@ export declare function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, options?: { forceNow?: Date; fieldName?: string; -}): import("@kbn/es-query").RangeFilter | undefined; +}): import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter | undefined; ``` ## Parameters @@ -23,5 +23,5 @@ export declare function getTime(indexPattern: IIndexPattern | undefined, timeRan Returns: -`import("@kbn/es-query").RangeFilter | undefined` +`import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter | undefined` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md index b37d0555194fc..fa95ea72035dd 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md @@ -8,14 +8,18 @@ ```typescript esFilters: { - buildQueryFilter: (query: any, index: string, alias: string) => import("@kbn/es-query/target_types/filters/build_filters").QueryStringFilter; + buildQueryFilter: (query: (Record & { + query_string?: { + query: string; + } | undefined; + }) | undefined, index: string, alias: string) => import("@kbn/es-query").QueryStringFilter; buildCustomFilter: typeof import("@kbn/es-query").buildCustomFilter; buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("@kbn/es-query").Filter; buildExistsFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").ExistsFilter; buildFilter: typeof import("@kbn/es-query").buildFilter; - buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter; - buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: string[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; - buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter; + buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").ScriptedPhraseFilter; + buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: (string | number | boolean)[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; + buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter; isFilterDisabled: (filter: import("@kbn/es-query").Filter) => boolean; } ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.gettime.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.gettime.md index 7f2267aff7049..168be5db779a2 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.gettime.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.gettime.md @@ -10,7 +10,7 @@ export declare function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, options?: { forceNow?: Date; fieldName?: string; -}): import("@kbn/es-query").RangeFilter | undefined; +}): import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter | undefined; ``` ## Parameters @@ -23,5 +23,5 @@ export declare function getTime(indexPattern: IIndexPattern | undefined, timeRan Returns: -`import("@kbn/es-query").RangeFilter | undefined` +`import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter | undefined` diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 3f9b88a4a64c3..d2178550a9100 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -738,24 +738,28 @@ export const esFilters: { FILTERS: typeof import("@kbn/es-query").FILTERS; FilterStateStore: typeof FilterStateStore; buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("@kbn/es-query").Filter; - buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: string[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; + buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: (string | number | boolean)[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; buildExistsFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").ExistsFilter; - buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter; - buildQueryFilter: (query: any, index: string, alias: string) => import("@kbn/es-query/target_types/filters/build_filters").QueryStringFilter; - buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter; - isPhraseFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").PhraseFilter; - isExistsFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").ExistsFilter; - isPhrasesFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").PhrasesFilter; - isRangeFilter: (filter?: import("@kbn/es-query").ExistsFilter | import("@kbn/es-query").PhrasesFilter | import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").MatchAllFilter | import("@kbn/es-query").MissingFilter | import("@kbn/es-query").RangeFilter | undefined) => filter is import("@kbn/es-query").RangeFilter; - isMatchAllFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").MatchAllFilter; - isMissingFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query").MissingFilter; - isQueryStringFilter: (filter: import("@kbn/es-query").FieldFilter) => filter is import("@kbn/es-query/target_types/filters/build_filters").QueryStringFilter; + buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").ScriptedPhraseFilter; + buildQueryFilter: (query: (Record & { + query_string?: { + query: string; + } | undefined; + }) | undefined, index: string, alias: string) => import("@kbn/es-query").QueryStringFilter; + buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter; + isPhraseFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").PhraseFilter; + isExistsFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").ExistsFilter; + isPhrasesFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").PhrasesFilter; + isRangeFilter: (filter?: import("@kbn/es-query").Filter | undefined) => filter is import("@kbn/es-query").RangeFilter; + isMatchAllFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").MatchAllFilter; + isMissingFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").MissingFilter; + isQueryStringFilter: (filter: import("@kbn/es-query").Filter) => filter is import("@kbn/es-query").QueryStringFilter; isFilterPinned: (filter: import("@kbn/es-query").Filter) => boolean | undefined; toggleFilterNegated: (filter: import("@kbn/es-query").Filter) => { meta: { negate: boolean; - alias: string | null; - disabled: boolean; + alias?: string | null | undefined; + disabled?: boolean | undefined; controlledBy?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; @@ -767,11 +771,11 @@ export const esFilters: { $state?: { store: FilterStateStore; } | undefined; - query?: any; + query?: Record | undefined; }; disableFilter: (filter: import("@kbn/es-query").Filter) => import("@kbn/es-query").Filter; getPhraseFilterField: (filter: import("@kbn/es-query").PhraseFilter) => string; - getPhraseFilterValue: (filter: import("@kbn/es-query").PhraseFilter) => string | number | boolean; + getPhraseFilterValue: (filter: import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").ScriptedPhraseFilter) => string | number | boolean; getDisplayValueFromFilter: typeof getDisplayValueFromFilter; compareFilters: (first: import("@kbn/es-query").Filter | import("@kbn/es-query").Filter[], second: import("@kbn/es-query").Filter | import("@kbn/es-query").Filter[], comparatorOptions?: import("@kbn/es-query").FilterCompareOptions | undefined) => boolean; COMPARE_ALL_OPTIONS: import("@kbn/es-query").FilterCompareOptions; @@ -1001,7 +1005,7 @@ export function getSearchParamsFromRequest(searchRequest: SearchRequest, depende export function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, options?: { forceNow?: Date; fieldName?: string; -}): import("@kbn/es-query").RangeFilter | undefined; +}): import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter | undefined; // Warning: (ae-missing-release-tag) "IAggConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 85994d0f172b5..503d19771c6ab 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -108,14 +108,18 @@ export const ES_SEARCH_STRATEGY = "es"; // // @public (undocumented) export const esFilters: { - buildQueryFilter: (query: any, index: string, alias: string) => import("@kbn/es-query/target_types/filters/build_filters").QueryStringFilter; + buildQueryFilter: (query: (Record & { + query_string?: { + query: string; + } | undefined; + }) | undefined, index: string, alias: string) => import("@kbn/es-query").QueryStringFilter; buildCustomFilter: typeof import("@kbn/es-query").buildCustomFilter; buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("@kbn/es-query").Filter; buildExistsFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").ExistsFilter; buildFilter: typeof import("@kbn/es-query").buildFilter; - buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter; - buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: string[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; - buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter; + buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: string | number | boolean, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter | import("@kbn/es-query").ScriptedPhraseFilter; + buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: (string | number | boolean)[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; + buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter; isFilterDisabled: (filter: import("@kbn/es-query").Filter) => boolean; }; @@ -199,7 +203,7 @@ export function getEsQueryConfig(config: KibanaConfig): EsQueryConfig_2; export function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, options?: { forceNow?: Date; fieldName?: string; -}): import("@kbn/es-query").RangeFilter | undefined; +}): import("@kbn/es-query").RangeFilter | import("@kbn/es-query").ScriptedRangeFilter | import("@kbn/es-query/target_types/filters/build_filters").MatchAllRangeFilter | undefined; // Warning: (ae-forgotten-export) The symbol "IKibanaSearchRequest" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ISearchRequestParams" needs to be exported by the entry point index.d.ts From 9c7e8d10568328245985b86e767c01ac23d30bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Thu, 26 Aug 2021 14:47:32 +0100 Subject: [PATCH 21/60] Rely on types from the data plugin instead of creating new one --- .../components/field_editor_flyout_content.tsx | 17 ++++++++++------- .../field_editor_flyout_content_container.tsx | 15 ++++++++------- .../public/shared_imports.ts | 1 + .../index_pattern_field_editor/public/types.ts | 7 ------- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx index 20ccd59ae878f..bb7881508b84f 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx @@ -20,10 +20,14 @@ import { EuiText, } from '@elastic/eui'; -import type { Field, CompositeField } from '../types'; +import type { Field } from '../types'; import { RuntimeFieldPainlessError } from '../lib'; import { euiFlyoutClassname } from '../constants'; -import type { RuntimeField, EnhancedRuntimeField } from '../shared_imports'; +import type { + RuntimeField, + EnhancedRuntimeField, + RuntimeCompositeWithSubFields, +} from '../shared_imports'; import { FlyoutPanels } from './flyout_panels'; import { useFieldEditorContext } from './field_editor_context'; import { FieldEditor, FieldEditorFormState } from './field_editor/field_editor'; @@ -51,7 +55,7 @@ export interface Props { /** * Handler for the "save" footer button */ - onSave: (field: Field | CompositeField) => void; + onSave: (field: Field | RuntimeCompositeWithSubFields) => void; /** * Handler for the "cancel" footer button */ @@ -122,7 +126,7 @@ const FieldEditorFlyoutContentComponent = ({ }, [isFormModified]); const addSubfieldsToField = useCallback( - (_field: Field): Field | CompositeField => { + (_field: Field): Field | RuntimeCompositeWithSubFields => { if (_field.type === 'composite' && fieldsInScript.length > 0) { const subFields = fieldsInScript.reduce((acc, subFieldName) => { const subField: EnhancedRuntimeField = { @@ -136,10 +140,9 @@ const FieldEditorFlyoutContentComponent = ({ return acc; }, {} as Record); - const updatedField: CompositeField = { - type: 'composite', + const updatedField: RuntimeCompositeWithSubFields = { name: _field.name, - script: _field.script, + script: _field.script!, subFields, }; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index 9a00f0fb62b0d..fff462d448b44 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -16,8 +16,9 @@ import { IndexPattern, DataPublicPluginStart, UsageCollectionStart, + RuntimeCompositeWithSubFields, } from '../shared_imports'; -import type { Field, PluginStart, InternalFieldType, CompositeField } from '../types'; +import type { Field, PluginStart, InternalFieldType } from '../types'; import { pluginName } from '../constants'; import { deserializeField, getRuntimeFieldValidator, getLinks, ApiService } from '../lib'; import { @@ -117,7 +118,7 @@ export const FieldEditorFlyoutContentContainer = ({ ); const saveCompositeRuntime = useCallback( - (updatedField: CompositeField): IndexPatternField[] => { + (updatedField: RuntimeCompositeWithSubFields): IndexPatternField[] => { if (field?.type !== undefined && field?.type !== 'composite') { // A previous runtime field is now a runtime composite indexPattern.removeRuntimeField(field.name); @@ -140,7 +141,7 @@ export const FieldEditorFlyoutContentContainer = ({ const saveRuntimeField = useCallback( (updatedField: Field): [IndexPatternField] => { if (field?.type !== undefined && field?.type === 'object') { - // A previous runtime object is now a runtime field + // A previous runtime composite is now a runtime field indexPattern.removeRuntimeComposite(field.name); } else if (field?.name && field.name !== updatedField.name) { // rename an existing runtime field @@ -154,7 +155,7 @@ export const FieldEditorFlyoutContentContainer = ({ ); const saveField = useCallback( - async (updatedField: Field | CompositeField) => { + async (updatedField: Field | RuntimeCompositeWithSubFields) => { setIsSaving(true); if (fieldTypeToProcess === 'runtime') { @@ -171,9 +172,9 @@ export const FieldEditorFlyoutContentContainer = ({ try { const editedFields: IndexPatternField[] = - updatedField.type === 'composite' - ? saveCompositeRuntime(updatedField as CompositeField) - : saveRuntimeField(updatedField); + (updatedField as RuntimeCompositeWithSubFields).subFields !== undefined + ? saveCompositeRuntime(updatedField as RuntimeCompositeWithSubFields) + : saveRuntimeField(updatedField as Field); await indexPatternService.updateSavedObject(indexPattern); diff --git a/src/plugins/index_pattern_field_editor/public/shared_imports.ts b/src/plugins/index_pattern_field_editor/public/shared_imports.ts index afcd70ef50378..7efd029d8c304 100644 --- a/src/plugins/index_pattern_field_editor/public/shared_imports.ts +++ b/src/plugins/index_pattern_field_editor/public/shared_imports.ts @@ -16,6 +16,7 @@ export { KBN_FIELD_TYPES, ES_FIELD_TYPES, EnhancedRuntimeField, + RuntimeCompositeWithSubFields, } from '../../data/common'; export { createKibanaReactContext, toMountPoint, CodeEditor } from '../../kibana_react/public'; diff --git a/src/plugins/index_pattern_field_editor/public/types.ts b/src/plugins/index_pattern_field_editor/public/types.ts index 3913cb183ba0f..06ca1bed21726 100644 --- a/src/plugins/index_pattern_field_editor/public/types.ts +++ b/src/plugins/index_pattern_field_editor/public/types.ts @@ -10,7 +10,6 @@ import { FunctionComponent } from 'react'; import { DataPublicPluginStart, - RuntimeField, UsageCollectionStart, EnhancedRuntimeField, } from './shared_imports'; @@ -47,12 +46,6 @@ export interface Field extends EnhancedRuntimeField { name: string; } -export interface CompositeField extends RuntimeField { - type: 'composite'; - name: string; - subFields: Record; -} - export type FieldFormatConfig = EnhancedRuntimeField['format']; export type CloseEditor = () => void; From a3f0d32f413ca207a7483cc43c382ae2550230e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Thu, 26 Aug 2021 14:51:57 +0100 Subject: [PATCH 22/60] Rename "parent" to "parentComposite" and prevent removing a runtime field with a parent --- .../index_patterns/index_pattern.ts | 22 ++++++++++++++----- .../data/common/index_patterns/types.ts | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index ba514195886c3..e57c10cddfdba 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -224,9 +224,9 @@ export class IndexPattern implements IIndexPattern { */ private getComputedRuntimeFields(): Record { return Object.entries(this.runtimeFieldMap).reduce((acc, [name, field]) => { - const { type, script, parent } = field; + const { type, script, parentComposite } = field; - if (parent !== undefined) { + if (parentComposite !== undefined) { return acc; } @@ -473,9 +473,9 @@ export class IndexPattern implements IIndexPattern { * @param runtimeField Runtime field definition */ addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): IndexPatternField { - const { type, script, parent, customLabel, format, popularity } = enhancedRuntimeField; + const { type, script, parentComposite, customLabel, format, popularity } = enhancedRuntimeField; - const runtimeField: RuntimeField = { type, script, parent }; + const runtimeField: RuntimeField = { type, script, parentComposite }; let fieldCreated: IndexPatternField; const existingField = this.getFieldByName(name); @@ -544,7 +544,14 @@ export class IndexPattern implements IIndexPattern { */ removeRuntimeField(name: string) { const existingField = this.getFieldByName(name); + if (existingField) { + if (existingField.runtimeField?.parentComposite !== undefined) { + throw new Error( + `Can't remove runtime field ["${name}"] as it belongs to the composite runtime ["${existingField.runtimeField.parentComposite}"]` + ); + } + if (existingField.isMapped) { // mapped field, remove runtimeField def existingField.runtimeField = undefined; @@ -575,7 +582,10 @@ export class IndexPattern implements IIndexPattern { const fieldsCreated: IndexPatternField[] = []; for (const [subFieldName, subField] of Object.entries(subFields)) { - const field = this.addRuntimeField(`${name}.${subFieldName}`, { ...subField, parent: name }); + const field = this.addRuntimeField(`${name}.${subFieldName}`, { + ...subField, + parentComposite: name, + }); fieldsCreated.push(field); } @@ -620,7 +630,7 @@ export class IndexPattern implements IIndexPattern { type: field.type as RuntimeType, customLabel: field.customLabel, popularity: field.count, - parent: name, + parentComposite: name, format: this.getFormatterForFieldNoDefault(field.name)?.toJSON(), }; diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 1ce502a32757d..480c476da0ce3 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -25,7 +25,7 @@ export interface RuntimeField { script?: { source: string; }; - parent?: string; + parentComposite?: string; fields?: Record< string, { From 05d13d98008bd1462e683834334fcc2cbe4a4638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Thu, 26 Aug 2021 14:55:19 +0100 Subject: [PATCH 23/60] Fix TS issue --- .../public/lib/runtime_field_validation.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.test.ts b/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.test.ts index b25d47b3d0d15..485e4960605cd 100644 --- a/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.test.ts +++ b/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.test.ts @@ -13,7 +13,7 @@ const dataStart = dataPluginMock.createStartContract(); const { search } = dataStart; const runtimeField = { - type: 'keyword', + type: 'keyword' as const, script: { source: 'emit("hello")', }, From c466d4966dbd794f4ece84eb48266f3b5eb8d8cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Thu, 26 Aug 2021 15:30:18 +0100 Subject: [PATCH 24/60] Fix jest snapshot --- .../index_patterns/__snapshots__/index_pattern.test.ts.snap | 2 +- .../__snapshots__/index_patterns.test.ts.snap | 1 + .../index_pattern_field_editor/public/open_delete_modal.tsx | 6 ++++-- .../index_pattern_field_editor/public/open_editor.tsx | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap index 84ad0e4524399..3ad1c44c56eb4 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap +++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap @@ -776,6 +776,7 @@ Object { }, "id": "test-pattern", "intervalName": undefined, + "runtimeCompositeMap": Object {}, "runtimeFieldMap": Object { "runtime_field": Object { "script": Object { @@ -784,7 +785,6 @@ Object { "type": "keyword", }, }, - "runtimeObjectMap": Object {}, "sourceFilters": undefined, "timeFieldName": "time", "title": "title", diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap index af9499bd7e263..e348c10a5a482 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap +++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap @@ -37,6 +37,7 @@ Object { "fields": Object {}, "id": "id", "intervalName": undefined, + "runtimeCompositeMap": Object {}, "runtimeFieldMap": Object { "aRuntimeField": Object { "script": Object { diff --git a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx index 578ed785df86c..2620f65d2d568 100644 --- a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx @@ -42,9 +42,11 @@ export const getFieldDeleteModalOpener = ({ }: Dependencies) => (options: OpenFieldDeleteModalOptions): CloseEditor => { if (typeof options.fieldName === 'string') { const fieldToDelete = options.ctx.indexPattern.getFieldByName(options.fieldName); - const parent = fieldToDelete?.runtimeField?.parent; + const parentComposite = fieldToDelete?.runtimeField?.parentComposite; const doesBelongToCompositeField = - parent === undefined ? false : options.ctx.indexPattern.getRuntimeComposite(parent) !== null; + parentComposite === undefined + ? false + : options.ctx.indexPattern.getRuntimeComposite(parentComposite) !== null; if (doesBelongToCompositeField) { console.log( // eslint-disable-line diff --git a/src/plugins/index_pattern_field_editor/public/open_editor.tsx b/src/plugins/index_pattern_field_editor/public/open_editor.tsx index c72325ba25cc7..a67c4361178c2 100644 --- a/src/plugins/index_pattern_field_editor/public/open_editor.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_editor.tsx @@ -104,7 +104,7 @@ export const getFieldEditorOpener = ({ const fieldTypeToProcess: InternalFieldType = isNewRuntimeField || isExistingRuntimeField ? 'runtime' : 'concrete'; - if (field?.runtimeField?.parent !== undefined) { + if (field?.runtimeField?.parentComposite !== undefined) { console.log( // eslint-disable-line 'TODO: display a modal to indicate that this field needs to be edited through its parent.' ); From c582c99692a84dc962fbd76ab8ef81d7d7d5bde9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Thu, 26 Aug 2021 15:56:31 +0100 Subject: [PATCH 25/60] Fix TS issue --- src/plugins/vis_type_vega/public/data_model/search_api.ts | 1 + x-pack/plugins/lens/server/routes/existing_fields.ts | 1 + x-pack/plugins/lens/server/routes/field_stats.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/src/plugins/vis_type_vega/public/data_model/search_api.ts b/src/plugins/vis_type_vega/public/data_model/search_api.ts index efdbf96e54f05..6ed11eed37960 100644 --- a/src/plugins/vis_type_vega/public/data_model/search_api.ts +++ b/src/plugins/vis_type_vega/public/data_model/search_api.ts @@ -32,6 +32,7 @@ export const extendSearchParamsWithRuntimeFields = async ( const indexPattern = (await indexPatterns.find(indexPatternString)).find( (index) => index.title === indexPatternString ); + // @ts-expect-error The MappingRuntimeFieldType from @elastic/elasticsearch does not expose the "composite" runtime type yet runtimeMappings = indexPattern?.getComputedFields().runtimeFields; } diff --git a/x-pack/plugins/lens/server/routes/existing_fields.ts b/x-pack/plugins/lens/server/routes/existing_fields.ts index f35b0a7f23179..a654608f97cc4 100644 --- a/x-pack/plugins/lens/server/routes/existing_fields.ts +++ b/x-pack/plugins/lens/server/routes/existing_fields.ts @@ -201,6 +201,7 @@ async function fetchIndexPatternStats({ _source: false, runtime_mappings: runtimeFields.reduce((acc, field) => { if (!field.runtimeField) return acc; + // @ts-expect-error The MappingRuntimeField from @elastic/elasticsearch does not expose the "composite" runtime type yet acc[field.name] = field.runtimeField; return acc; }, {} as Record), diff --git a/x-pack/plugins/lens/server/routes/field_stats.ts b/x-pack/plugins/lens/server/routes/field_stats.ts index 7103e395eabdc..161a22328d74e 100644 --- a/x-pack/plugins/lens/server/routes/field_stats.ts +++ b/x-pack/plugins/lens/server/routes/field_stats.ts @@ -83,6 +83,7 @@ export async function initFieldsRoute(setup: CoreSetup) { .filter((f) => f.runtimeField) .reduce((acc, f) => { if (!f.runtimeField) return acc; + // @ts-expect-error The MappingRuntimeField from @elastic/elasticsearch does not expose the "composite" runtime type yet acc[f.name] = f.runtimeField; return acc; }, {} as Record); From 6f9d5a11735e6b0f7ac546d420284370e5abcb48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 30 Aug 2021 11:20:40 +0100 Subject: [PATCH 26/60] Improve types and add comments --- .../index_patterns/index_pattern.ts | 11 ++--- .../data/common/index_patterns/types.ts | 43 +++++++++++++++---- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index e57c10cddfdba..ce45deaff3f32 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -16,6 +16,7 @@ import type { RuntimeType, RuntimeComposite, RuntimeCompositeWithSubFields, + ESRuntimeField, } from '../types'; import { DuplicateField } from '../../../../kibana_utils/common'; @@ -204,7 +205,7 @@ export class IndexPattern implements IIndexPattern { }; }); - const runtimeFields = { + const runtimeFields: Record = { ...this.getComputedRuntimeFields(), ...this.getComputedRuntimeComposites(), }; @@ -222,7 +223,7 @@ export class IndexPattern implements IIndexPattern { * from a parent composite runtime field. * @returns A map of runtime fields */ - private getComputedRuntimeFields(): Record { + private getComputedRuntimeFields(): Record { return Object.entries(this.runtimeFieldMap).reduce((acc, [name, field]) => { const { type, script, parentComposite } = field; @@ -263,7 +264,7 @@ export class IndexPattern implements IIndexPattern { * * @returns A map of runtime fields */ - private getComputedRuntimeComposites(): Record { + private getComputedRuntimeComposites(): Record { return Object.entries(this.runtimeCompositeMap).reduce((acc, [name, runtimeComposite]) => { const { script, subFields } = runtimeComposite; @@ -290,7 +291,7 @@ export class IndexPattern implements IIndexPattern { return acc; } - const runtimeFieldRequest: RuntimeField = { + const runtimeFieldRequest: ESRuntimeField = { type: 'composite', script, fields, @@ -526,7 +527,7 @@ export class IndexPattern implements IIndexPattern { * Replaces all existing runtime fields with new fields * @param newFields */ - replaceAllRuntimeFields(newFields: Record) { + replaceAllRuntimeFields(newFields: Record) { const oldRuntimeFieldNames = Object.keys(this.runtimeFieldMap); oldRuntimeFieldNames.forEach((name) => { this.removeRuntimeField(name); diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 480c476da0ce3..cf3e56427b941 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -11,35 +11,55 @@ import { ToastInputFields, ErrorToastOptions } from 'src/core/public/notificatio // eslint-disable-next-line import type { SavedObject } from 'src/core/server'; import { IFieldType } from './fields'; -import { RUNTIME_FIELD_TYPES } from './constants'; import { SerializedFieldFormat } from '../../../expressions/common'; import { KBN_FIELD_TYPES, IndexPatternField } from '..'; import { FieldFormat } from '../../../field_formats/common'; export type FieldFormatMap = Record; -export type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; +export type RuntimeType = estypes.MappingRuntimeFieldType | 'composite'; -export interface RuntimeField { +/** + * The RuntimeField that will be sent in the ES Query "runtime_mappings" object + * We extends the object until @elastic/elasticsearch supports "composite" type + * and its "fields" object + */ +export interface ESRuntimeField extends Omit { type: RuntimeType; - script?: { - source: string; - }; - parentComposite?: string; fields?: Record< string, { + // It is not recursive, we can't create a composite inside a composite. type: Omit; } >; } -export interface EnhancedRuntimeField extends RuntimeField { +/** + * The RuntimeField which is saved in the Data View saved object. We extend it to + * keep a reference to a possible parent composite object. + */ +export interface RuntimeField extends ESRuntimeField { + parentComposite?: string; +} + +/** + * Runtime fields are like other fields when it comes to formatting or giving + * them a custom label. When adding a new runtime field in the Data view we allow the + * consumer to pass along a "format", "customLabel" or "popularity". + */ +export interface EnhancedRuntimeField extends Omit { format?: SerializedFieldFormat; customLabel?: string; popularity?: number; } +/** + * When we add a runtime field of "composite" type we are actually adding a _holder_ + * object with runtime fields inside of it. + * The RuntimeComposite interface is this holder of fields. + * It has a name, a script and an array references to the runtime fields it holds. + */ export interface RuntimeComposite { name: string; script: { @@ -48,6 +68,13 @@ export interface RuntimeComposite { subFields: string[]; } +/** + * This is the same as the RuntimeComposite interface but instead of + * returning an array of references to the subFields we return a **map** of subfields + * with their possible format, custom label and popularity. + * + * @see {@link RuntimeComposite} + */ export type RuntimeCompositeWithSubFields = Omit & { subFields: Record; }; From 39095f81df886785c7d5de2d4e22389eabb12736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 30 Aug 2021 12:30:38 +0100 Subject: [PATCH 27/60] Address CR changes --- .../index_patterns/index_pattern.ts | 123 ++++-------------- .../index_patterns/index_pattern_utils.ts | 97 ++++++++++++++ 2 files changed, 119 insertions(+), 101 deletions(-) create mode 100644 src/plugins/data/common/index_patterns/index_patterns/index_pattern_utils.ts diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index ce45deaff3f32..af9df448f6d93 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -27,6 +27,7 @@ import { flattenHitWrapper } from './flatten_hit'; import { FieldFormatsStartCommon, FieldFormat } from '../../../../field_formats/common'; import { IndexPatternSpec, TypeMeta, SourceFilter, IndexPatternFieldMap } from '../types'; import { SerializedFieldFormat } from '../../../../expressions/common'; +import { getRuntimeFieldsFromMap, getRuntimeCompositeFieldsFromMap } from './index_pattern_utils'; interface IndexPatternDeps { spec?: IndexPatternSpec; @@ -206,8 +207,11 @@ export class IndexPattern implements IIndexPattern { }); const runtimeFields: Record = { - ...this.getComputedRuntimeFields(), - ...this.getComputedRuntimeComposites(), + ...getRuntimeFieldsFromMap(this.runtimeFieldMap), + ...getRuntimeCompositeFieldsFromMap( + this.runtimeCompositeMap, + this.getRuntimeField.bind(this) + ), }; return { @@ -218,92 +222,6 @@ export class IndexPattern implements IIndexPattern { }; } - /** - * Method to aggregate all the runtime fields which are **not** created - * from a parent composite runtime field. - * @returns A map of runtime fields - */ - private getComputedRuntimeFields(): Record { - return Object.entries(this.runtimeFieldMap).reduce((acc, [name, field]) => { - const { type, script, parentComposite } = field; - - if (parentComposite !== undefined) { - return acc; - } - - const runtimeFieldRequest: RuntimeField = { - type, - script, - }; - - return { - ...acc, - [name]: runtimeFieldRequest, - }; - }, {}); - } - - /** - * This method reads all the runtime composite fields - * and aggregate the subFields - * - * { - * "compositeName": { - * "type": "composite", - * "script": "emit(...)" // script that emits multiple values - * "fields": { // map of subFields available in the Query - * "field_1": { - * "type": "keyword" - * }, - * "field_2": { - * "type": "ip" - * }, - * } - * } - * } - * - * @returns A map of runtime fields - */ - private getComputedRuntimeComposites(): Record { - return Object.entries(this.runtimeCompositeMap).reduce((acc, [name, runtimeComposite]) => { - const { script, subFields } = runtimeComposite; - - // Aggregate all the subFields belonging to this runtimeComposite - const fields: Record = subFields.reduce( - (accFields, subFieldName) => { - const subField = this.getRuntimeField(`${name}.${subFieldName}`); - - if (!subField) { - return accFields; - } - - return { - ...accFields, - [subFieldName]: { type: subField.type }, - }; - }, - {} as Record - ); - - if (Object.keys(fields).length === 0) { - // This should never happen, but sending a composite runtime field - // with an empty "fields" will break the Query - return acc; - } - - const runtimeFieldRequest: ESRuntimeField = { - type: 'composite', - script, - fields, - }; - - return { - ...acc, - [name]: runtimeFieldRequest, - }; - }, {}); - } - /** * Create static representation of index pattern */ @@ -469,22 +387,24 @@ export class IndexPattern implements IIndexPattern { /** * Add a runtime field - Appended to existing mapped field or a new field is - * created as appropriate + * created as appropriate. We can pass along with the fields a "format" definition or "customLabel" to avoid calling + * separately `setFieldCustomLabel()` and `setFieldFormat()`. * @param name Field name - * @param runtimeField Runtime field definition + * @param enhancedRuntimeField Runtime field definition */ addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): IndexPatternField { const { type, script, parentComposite, customLabel, format, popularity } = enhancedRuntimeField; const runtimeField: RuntimeField = { type, script, parentComposite }; + this.runtimeFieldMap[name] = runtimeField; - let fieldCreated: IndexPatternField; + // Create the field if it does not exist or update an existing one + let createdField: IndexPatternField | undefined; const existingField = this.getFieldByName(name); if (existingField) { existingField.runtimeField = runtimeField; - fieldCreated = existingField; } else { - fieldCreated = this.fields.add({ + createdField = this.fields.add({ name, runtimeField, type: castEsToKbnFieldTypeName(type), @@ -494,17 +414,16 @@ export class IndexPattern implements IIndexPattern { readFromDocValues: false, }); } - this.runtimeFieldMap[name] = runtimeField; + // Apply configuration to the field this.setFieldCustomLabel(name, customLabel); - if (format) { this.setFieldFormat(name, format); } else { this.deleteFieldFormat(name); } - return fieldCreated; + return existingField ?? createdField!; } /** @@ -576,6 +495,8 @@ export class IndexPattern implements IIndexPattern { throw new Error(`Can't save runtime composite [name = ${name}] without subfields.`); } + // We first remove the runtime composite with the same name which will remove all of its subFields. + // This guarantees that we don't leave behind orphan runtime fields (with a "compositeParent"). this.removeRuntimeComposite(name); const { script, subFields } = runtimeComposite; @@ -613,13 +534,13 @@ export class IndexPattern implements IIndexPattern { * @param name */ getRuntimeCompositeWithSubFields(name: string): RuntimeCompositeWithSubFields | null { - const existingRuntimeComposite = this.runtimeCompositeMap[name]; + const runtimeComposite = this.getRuntimeComposite(name); - if (!existingRuntimeComposite) { + if (!runtimeComposite) { return null; } - const subFields = existingRuntimeComposite.subFields.reduce((acc, subFieldName) => { + const subFields = runtimeComposite.subFields.reduce((acc, subFieldName) => { const field = this.getFieldByName(subFieldName); if (!field) { @@ -642,7 +563,7 @@ export class IndexPattern implements IIndexPattern { }, {} as Record); return { - ...existingRuntimeComposite, + ...runtimeComposite, subFields, }; } @@ -655,7 +576,7 @@ export class IndexPattern implements IIndexPattern { const existingRuntimeComposite = this.getRuntimeComposite(name); if (!!existingRuntimeComposite) { - // Remove all previous subFields + // Remove all subFields for (const subFieldName of existingRuntimeComposite.subFields) { this.removeRuntimeField(`${name}.${subFieldName}`); } diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern_utils.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern_utils.ts new file mode 100644 index 0000000000000..17b50ac64a89e --- /dev/null +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern_utils.ts @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { RuntimeField, RuntimeType, RuntimeComposite, ESRuntimeField } from '../types'; + +/** + * Method to aggregate all the runtime fields which are **not** created + * from a parent composite runtime field. + * @returns A map of runtime fields + */ +export const getRuntimeFieldsFromMap = ( + runtimeFieldMap: Record +): Record => { + return Object.entries(runtimeFieldMap).reduce((acc, [name, field]) => { + const { type, script, parentComposite } = field; + + if (parentComposite !== undefined) { + return acc; + } + + const runtimeFieldRequest: RuntimeField = { + type, + script, + }; + + return { + ...acc, + [name]: runtimeFieldRequest, + }; + }, {}); +}; + +/** + * This method reads all the runtime composite fields + * and aggregate the subFields + * + * { + * "compositeName": { + * "type": "composite", + * "script": "emit(...)" // script that emits multiple values + * "fields": { // map of subFields available in the Query + * "field_1": { + * "type": "keyword" + * }, + * "field_2": { + * "type": "ip" + * }, + * } + * } + * } + * + * @returns A map of runtime fields + */ +export const getRuntimeCompositeFieldsFromMap = ( + runtimeCompositeMap: Record, + runtimeFieldGetter: (name: string) => RuntimeField | null +): Record => { + return Object.entries(runtimeCompositeMap).reduce((acc, [name, runtimeComposite]) => { + const { script, subFields } = runtimeComposite; + + // Aggregate all the subFields belonging to this runtimeComposite + const fields: ESRuntimeField['fields'] = subFields.reduce((accFields, subFieldName) => { + const subField = runtimeFieldGetter(`${name}.${subFieldName}`); + + if (!subField) { + return accFields; + } + + return { + ...accFields, + [subFieldName]: { type: subField.type }, + }; + }, {} as Record); + + if (Object.keys(fields).length === 0) { + // This should never happen, but sending a composite runtime field + // with an empty "fields" will break the Query + return acc; + } + + const runtimeFieldRequest: ESRuntimeField = { + type: 'composite', + script, + fields, + }; + + return { + ...acc, + [name]: runtimeFieldRequest, + }; + }, {}); +}; From 78127ac8bace23f3ae89f71554ac35a9a8410b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 30 Aug 2021 13:01:32 +0100 Subject: [PATCH 28/60] Update jest snapshot --- .../discover_index_pattern_management.test.tsx.snap | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap b/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap index 3ad902ed22fe8..06adad0b51e2b 100644 --- a/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap +++ b/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap @@ -664,6 +664,7 @@ exports[`Discover IndexPattern Management renders correctly 1`] = ` ], "originalSavedObjectBody": Object {}, "resetOriginalSavedObjectBody": [Function], + "runtimeCompositeMap": Object {}, "runtimeFieldMap": Object {}, "setFieldFormat": [Function], "shortDotsEnable": false, From 768e22911b35b09bc3a7a58dd41f377137c2f970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 30 Aug 2021 13:01:59 +0100 Subject: [PATCH 29/60] Fix TS issues and update server schema for runtime field --- .../data/common/index_patterns/types.ts | 4 +++- .../routes/runtime_fields/put_runtime_field.ts | 6 +++++- .../runtime_fields/update_runtime_field.ts | 18 ++++++++++++------ .../index_patterns/routes/util/schemas.ts | 7 +++++++ 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index cf3e56427b941..632559f4b84c0 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -38,8 +38,10 @@ export interface ESRuntimeField extends Omit { + script?: estypes.InlineScript; parentComposite?: string; } diff --git a/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts b/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts index a5e92fa5a36ec..f14e9262de9ae 100644 --- a/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts +++ b/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts @@ -7,6 +7,7 @@ */ import { schema } from '@kbn/config-schema'; +import { EnhancedRuntimeField } from 'src/plugins/data/common'; import { handleErrors } from '../util/handle_errors'; import { runtimeFieldSpecSchema } from '../util/schemas'; import { IRouter, StartServicesAccessor } from '../../../../../../core/server'; @@ -44,7 +45,10 @@ export const registerPutRuntimeFieldRoute = ( elasticsearchClient ); const id = req.params.id; - const { name, runtimeField } = req.body; + const { name, runtimeField } = req.body as { + name: string; + runtimeField: EnhancedRuntimeField; + }; const indexPattern = await indexPatternsService.get(id); diff --git a/src/plugins/data/server/index_patterns/routes/runtime_fields/update_runtime_field.ts b/src/plugins/data/server/index_patterns/routes/runtime_fields/update_runtime_field.ts index 3f3aae46c4388..13f88188dcd5d 100644 --- a/src/plugins/data/server/index_patterns/routes/runtime_fields/update_runtime_field.ts +++ b/src/plugins/data/server/index_patterns/routes/runtime_fields/update_runtime_field.ts @@ -7,7 +7,7 @@ */ import { schema } from '@kbn/config-schema'; -import { RuntimeField } from 'src/plugins/data/common'; +import { EnhancedRuntimeField } from 'src/plugins/data/common'; import { ErrorIndexPatternFieldNotFound } from '../../error'; import { handleErrors } from '../util/handle_errors'; import { runtimeFieldSpec, runtimeFieldSpecTypeSchema } from '../util/schemas'; @@ -53,7 +53,7 @@ export const registerUpdateRuntimeFieldRoute = ( ); const id = req.params.id; const name = req.params.name; - const runtimeField = req.body.runtimeField as Partial; + const runtimeField = req.body.runtimeField as Partial; const indexPattern = await indexPatternsService.get(id); const existingRuntimeField = indexPattern.getRuntimeField(name); @@ -62,11 +62,17 @@ export const registerUpdateRuntimeFieldRoute = ( throw new ErrorIndexPatternFieldNotFound(id, name); } - indexPattern.removeRuntimeField(name); - indexPattern.addRuntimeField(name, { - ...existingRuntimeField, + // We remove a possible format as the ES "format" (string) is different + // from our Kibana field format definition. + const { format, ...previousField } = existingRuntimeField; + + const updatedRuntimeField: EnhancedRuntimeField = { + ...previousField, ...runtimeField, - }); + }; + + indexPattern.removeRuntimeField(name); + indexPattern.addRuntimeField(name, updatedRuntimeField); await indexPatternsService.updateSavedObject(indexPattern); diff --git a/src/plugins/data/server/index_patterns/routes/util/schemas.ts b/src/plugins/data/server/index_patterns/routes/util/schemas.ts index 7fc1030e4eaee..a3a3cf5afbceb 100644 --- a/src/plugins/data/server/index_patterns/routes/util/schemas.ts +++ b/src/plugins/data/server/index_patterns/routes/util/schemas.ts @@ -72,6 +72,13 @@ export const runtimeFieldSpec = { source: schema.string(), }) ), + format: schema.maybe(serializedFieldFormatSchema), + customLabel: schema.maybe(schema.string()), + popularity: schema.maybe( + schema.number({ + min: 0, + }) + ), }; export const runtimeFieldSpecSchema = schema.object(runtimeFieldSpec); From 29a4edbeb061d92a20ee3d06ac0f393063e65f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 30 Aug 2021 13:22:25 +0100 Subject: [PATCH 30/60] Remove API docs --- ...public.indexpattern.addruntimecomposite.md | 25 ------------------- ...public.indexpattern.getruntimecomposite.md | 24 ------------------ ...attern.getruntimecompositewithsubfields.md | 24 ------------------ ...lic.indexpattern.removeruntimecomposite.md | 24 ------------------ ...expatternattributes.runtimecompositemap.md | 11 -------- ...ic.indexpatternspec.runtimecompositemap.md | 11 -------- ...-plugin-plugins-data-public.runtimetype.md | 11 -------- ...server.indexpattern.addruntimecomposite.md | 25 ------------------- ...server.indexpattern.getruntimecomposite.md | 24 ------------------ ...attern.getruntimecompositewithsubfields.md | 24 ------------------ ...ver.indexpattern.removeruntimecomposite.md | 24 ------------------ ...expatternattributes.runtimecompositemap.md | 11 -------- 12 files changed, 238 deletions(-) delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.runtimetype.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md deleted file mode 100644 index b0e1266ad2997..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [addRuntimeComposite](./kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md) - -## IndexPattern.addRuntimeComposite() method - -Create a runtime composite and add its subFields to the index pattern fields list - -Signature: - -```typescript -addRuntimeComposite(name: string, runtimeComposite: RuntimeCompositeWithSubFields): IndexPatternField[]; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| name | string | The runtime composite name | -| runtimeComposite | RuntimeCompositeWithSubFields | The runtime composite definition | - -Returns: - -`IndexPatternField[]` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md deleted file mode 100644 index 1e5db3903e43a..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [getRuntimeComposite](./kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md) - -## IndexPattern.getRuntimeComposite() method - -Returns runtime composite if exists - -Signature: - -```typescript -getRuntimeComposite(name: string): RuntimeComposite | null; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| name | string | | - -Returns: - -`RuntimeComposite | null` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md deleted file mode 100644 index 22f91d6695ce3..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [getRuntimeCompositeWithSubFields](./kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md) - -## IndexPattern.getRuntimeCompositeWithSubFields() method - -Returns runtime composite (if exists) with its subFields - -Signature: - -```typescript -getRuntimeCompositeWithSubFields(name: string): RuntimeCompositeWithSubFields | null; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| name | string | | - -Returns: - -`RuntimeCompositeWithSubFields | null` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md deleted file mode 100644 index 3fca980ad5d69..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [removeRuntimeComposite](./kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md) - -## IndexPattern.removeRuntimeComposite() method - -Remove a runtime composite with its associated subFields - -Signature: - -```typescript -removeRuntimeComposite(name: string): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| name | string | Runtime composite name to remove | - -Returns: - -`void` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md deleted file mode 100644 index 40e939013f157..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternAttributes](./kibana-plugin-plugins-data-public.indexpatternattributes.md) > [runtimeCompositeMap](./kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md) - -## IndexPatternAttributes.runtimeCompositeMap property - -Signature: - -```typescript -runtimeCompositeMap?: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md deleted file mode 100644 index 4667de1abdd9b..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) > [runtimeCompositeMap](./kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md) - -## IndexPatternSpec.runtimeCompositeMap property - -Signature: - -```typescript -runtimeCompositeMap?: Record; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.runtimetype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.runtimetype.md deleted file mode 100644 index 35d8d42ba7ab3..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.runtimetype.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RuntimeType](./kibana-plugin-plugins-data-public.runtimetype.md) - -## RuntimeType type - -Signature: - -```typescript -export declare type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md deleted file mode 100644 index a5dfa4fb50ba4..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [addRuntimeComposite](./kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md) - -## IndexPattern.addRuntimeComposite() method - -Create a runtime composite and add its subFields to the index pattern fields list - -Signature: - -```typescript -addRuntimeComposite(name: string, runtimeComposite: RuntimeCompositeWithSubFields): IndexPatternField[]; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| name | string | The runtime composite name | -| runtimeComposite | RuntimeCompositeWithSubFields | The runtime composite definition | - -Returns: - -`IndexPatternField[]` - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md deleted file mode 100644 index 7594ef8cdb804..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getRuntimeComposite](./kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md) - -## IndexPattern.getRuntimeComposite() method - -Returns runtime composite if exists - -Signature: - -```typescript -getRuntimeComposite(name: string): RuntimeComposite | null; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| name | string | | - -Returns: - -`RuntimeComposite | null` - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md deleted file mode 100644 index 8b6cda97ebb50..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getRuntimeCompositeWithSubFields](./kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md) - -## IndexPattern.getRuntimeCompositeWithSubFields() method - -Returns runtime composite (if exists) with its subFields - -Signature: - -```typescript -getRuntimeCompositeWithSubFields(name: string): RuntimeCompositeWithSubFields | null; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| name | string | | - -Returns: - -`RuntimeCompositeWithSubFields | null` - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md deleted file mode 100644 index 9cf8385152057..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [removeRuntimeComposite](./kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md) - -## IndexPattern.removeRuntimeComposite() method - -Remove a runtime composite with its associated subFields - -Signature: - -```typescript -removeRuntimeComposite(name: string): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| name | string | Runtime composite name to remove | - -Returns: - -`void` - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md deleted file mode 100644 index 6ecf4854873ef..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) > [runtimeCompositeMap](./kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md) - -## IndexPatternAttributes.runtimeCompositeMap property - -Signature: - -```typescript -runtimeCompositeMap?: string; -``` From 626a7a944a1daa5cb57e9515967dbf20552e1627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 30 Aug 2021 14:16:30 +0100 Subject: [PATCH 31/60] Fix TS issues --- .../common/log_sources/resolved_log_source_configuration.ts | 4 +++- .../plugins/transform/server/routes/api/field_histograms.ts | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/infra/common/log_sources/resolved_log_source_configuration.ts b/x-pack/plugins/infra/common/log_sources/resolved_log_source_configuration.ts index ee831d9a98eb9..e6c64aef53f91 100644 --- a/x-pack/plugins/infra/common/log_sources/resolved_log_source_configuration.ts +++ b/x-pack/plugins/infra/common/log_sources/resolved_log_source_configuration.ts @@ -109,6 +109,7 @@ const resolveRuntimeMappings = (indexPattern: IndexPattern): estypes.MappingRunt const runtimeMappingsFromIndexPattern = (Object.entries(runtimeFields) as ObjectEntries< typeof runtimeFields >).reduce( + // @ts-expect-error @elasticsearch/elasticsearch does not support yet "composite" type for runtime fields (accumulatedMappings, [runtimeFieldName, runtimeFieldSpec]) => ({ ...accumulatedMappings, [runtimeFieldName]: { @@ -117,7 +118,7 @@ const resolveRuntimeMappings = (indexPattern: IndexPattern): estypes.MappingRunt ? { script: { lang: 'painless', // required in the es types - source: runtimeFieldSpec.script.source, + source: (runtimeFieldSpec.script as estypes.InlineScript).source, }, } : {}), @@ -126,5 +127,6 @@ const resolveRuntimeMappings = (indexPattern: IndexPattern): estypes.MappingRunt {} ); + // @ts-expect-error @elasticsearch/elasticsearch does not support yet "composite" type for runtime fields return runtimeMappingsFromIndexPattern; }; diff --git a/x-pack/plugins/transform/server/routes/api/field_histograms.ts b/x-pack/plugins/transform/server/routes/api/field_histograms.ts index bfe2f47078569..7b947cf5b2fd9 100644 --- a/x-pack/plugins/transform/server/routes/api/field_histograms.ts +++ b/x-pack/plugins/transform/server/routes/api/field_histograms.ts @@ -41,6 +41,7 @@ export function registerFieldHistogramsRoutes({ router, license }: RouteDependen query, fields, samplerShardSize, + // @ts-expect-error @elasticsearch/elasticsearch does not support yet "composite" type for runtime fields runtimeMappings ); From 235d83cf0e53c574a7e3aa1000ffe77138856389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 30 Aug 2021 16:38:34 +0100 Subject: [PATCH 32/60] Fix functional test --- .../field_editor_flyout_content_container.tsx | 69 ++++++++++++++----- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index fff462d448b44..a40e847b7d946 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -154,28 +154,60 @@ export const FieldEditorFlyoutContentContainer = ({ [field?.name, field?.type, indexPattern] ); + const updateRuntimeField = useCallback( + (updatedField: Field | RuntimeCompositeWithSubFields): IndexPatternField[] => { + try { + usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'save_runtime'); + // eslint-disable-next-line no-empty + } catch {} + + return (updatedField as RuntimeCompositeWithSubFields).subFields !== undefined + ? saveCompositeRuntime(updatedField as RuntimeCompositeWithSubFields) + : saveRuntimeField(updatedField as Field); + }, + [usageCollection, saveCompositeRuntime, saveRuntimeField] + ); + + const updateConcreteField = useCallback( + (updatedField: Field): IndexPatternField[] => { + try { + usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'save_concrete'); + // eslint-disable-next-line no-empty + } catch {} + + const editedField = indexPattern.getFieldByName(updatedField.name); + + if (!editedField) { + throw new Error( + `Unable to find field named '${updatedField.name}' on index pattern '${indexPattern.title}'` + ); + } + + // Update custom label, popularity and format + indexPattern.setFieldCustomLabel(updatedField.name, updatedField.customLabel); + + editedField.count = updatedField.popularity || 0; + if (updatedField.format) { + indexPattern.setFieldFormat(updatedField.name, updatedField.format!); + } else { + indexPattern.deleteFieldFormat(updatedField.name); + } + + return [editedField]; + }, + [usageCollection, indexPattern] + ); + const saveField = useCallback( async (updatedField: Field | RuntimeCompositeWithSubFields) => { setIsSaving(true); - if (fieldTypeToProcess === 'runtime') { - try { - usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'save_runtime'); - // eslint-disable-next-line no-empty - } catch {} - } else { - try { - usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'save_concrete'); - // eslint-disable-next-line no-empty - } catch {} - } + const editedFields: IndexPatternField[] = + fieldTypeToProcess === 'runtime' + ? updateRuntimeField(updatedField) + : updateConcreteField(updatedField as Field); try { - const editedFields: IndexPatternField[] = - (updatedField as RuntimeCompositeWithSubFields).subFields !== undefined - ? saveCompositeRuntime(updatedField as RuntimeCompositeWithSubFields) - : saveRuntimeField(updatedField as Field); - await indexPatternService.updateSavedObject(indexPattern); const message = i18n.translate('indexPatternFieldEditor.deleteField.savedHeader', { @@ -200,9 +232,8 @@ export const FieldEditorFlyoutContentContainer = ({ indexPatternService, notifications, fieldTypeToProcess, - usageCollection, - saveRuntimeField, - saveCompositeRuntime, + updateConcreteField, + updateRuntimeField, ] ); From 6fbe79e28c65849f16edb82bb1735f0ed69861c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 30 Aug 2021 16:40:53 +0100 Subject: [PATCH 33/60] Small refactor --- .../field_editor_flyout_content_container.tsx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index a40e847b7d946..3e00720492c87 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -156,25 +156,15 @@ export const FieldEditorFlyoutContentContainer = ({ const updateRuntimeField = useCallback( (updatedField: Field | RuntimeCompositeWithSubFields): IndexPatternField[] => { - try { - usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'save_runtime'); - // eslint-disable-next-line no-empty - } catch {} - return (updatedField as RuntimeCompositeWithSubFields).subFields !== undefined ? saveCompositeRuntime(updatedField as RuntimeCompositeWithSubFields) : saveRuntimeField(updatedField as Field); }, - [usageCollection, saveCompositeRuntime, saveRuntimeField] + [saveCompositeRuntime, saveRuntimeField] ); const updateConcreteField = useCallback( (updatedField: Field): IndexPatternField[] => { - try { - usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'save_concrete'); - // eslint-disable-next-line no-empty - } catch {} - const editedField = indexPattern.getFieldByName(updatedField.name); if (!editedField) { @@ -195,11 +185,20 @@ export const FieldEditorFlyoutContentContainer = ({ return [editedField]; }, - [usageCollection, indexPattern] + [indexPattern] ); const saveField = useCallback( async (updatedField: Field | RuntimeCompositeWithSubFields) => { + try { + usageCollection.reportUiCounter( + pluginName, + METRIC_TYPE.COUNT, + fieldTypeToProcess === 'runtime' ? 'save_runtime' : 'save_concrete' + ); + // eslint-disable-next-line no-empty + } catch {} + setIsSaving(true); const editedFields: IndexPatternField[] = @@ -234,6 +233,7 @@ export const FieldEditorFlyoutContentContainer = ({ fieldTypeToProcess, updateConcreteField, updateRuntimeField, + usageCollection, ] ); From 2465b15ffb221c131db0b81732fe9d6ab707aa37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 31 Aug 2021 11:18:01 +0100 Subject: [PATCH 34/60] Use SerializedFieldFormat interface instead of FieldFormatConfig alias --- .../field_editor/form_fields/format_field.tsx | 11 ++++++++--- .../field_format_editor/field_format_editor.tsx | 10 +++++----- .../components/preview/field_preview_context.tsx | 6 +++--- .../public/shared_imports.ts | 2 ++ .../index_pattern_field_editor/public/types.ts | 2 -- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/format_field.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/format_field.tsx index 2ff4a48477def..3feef82533455 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/format_field.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/format_field.tsx @@ -8,11 +8,16 @@ import React, { useState, useEffect, useRef } from 'react'; import { EuiCallOut, EuiSpacer } from '@elastic/eui'; -import { UseField, useFormData, ES_FIELD_TYPES, useFormContext } from '../../../shared_imports'; +import { + UseField, + useFormData, + ES_FIELD_TYPES, + useFormContext, + SerializedFieldFormat, +} from '../../../shared_imports'; import { useFieldEditorContext } from '../../field_editor_context'; import { FormatSelectEditor } from '../../field_format_editor'; import type { FieldFormInternal } from '../field_editor'; -import type { FieldFormatConfig } from '../../../types'; export const FormatField = () => { const { indexPattern, uiSettings, fieldFormats, fieldFormatEditors } = useFieldEditorContext(); @@ -44,7 +49,7 @@ export const FormatField = () => { }, [type, getFields]); return ( - path="format"> + path="format"> {({ setValue, errors, value }) => { return ( <> diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx index 1c0c7ecba3b2b..bbd9fe88f818f 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx @@ -8,7 +8,6 @@ import React, { PureComponent } from 'react'; import { EuiCode, EuiFormRow, EuiSelect } from '@elastic/eui'; - import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { @@ -19,10 +18,11 @@ import { } from 'src/plugins/data/public'; import type { FieldFormatInstanceType } from 'src/plugins/field_formats/common'; import { CoreStart } from 'src/core/public'; + import { castEsToKbnFieldTypeName } from '../../../../data/public'; -import { FormatEditor } from './format_editor'; import { FormatEditorServiceStart } from '../../service'; -import { FieldFormatConfig } from '../../types'; +import { SerializedFieldFormat } from '../../shared_imports'; +import { FormatEditor } from './format_editor'; export interface FormatSelectEditorProps { esTypes: ES_FIELD_TYPES[]; @@ -30,9 +30,9 @@ export interface FormatSelectEditorProps { fieldFormatEditors: FormatEditorServiceStart['fieldFormatEditors']; fieldFormats: DataPublicPluginStart['fieldFormats']; uiSettings: CoreStart['uiSettings']; - onChange: (change?: FieldFormatConfig) => void; + onChange: (change?: SerializedFieldFormat) => void; onError: (error?: string) => void; - value?: FieldFormatConfig; + value?: SerializedFieldFormat; } interface FieldTypeFormat { diff --git a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx index 2c74f4899f159..03948bae1f684 100644 --- a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx @@ -20,9 +20,9 @@ import useDebounce from 'react-use/lib/useDebounce'; import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; -import type { FieldPreviewContext, FieldFormatConfig } from '../../types'; +import type { FieldPreviewContext } from '../../types'; import { parseEsError } from '../../lib/runtime_field_validation'; -import { RuntimeType, RuntimeField } from '../../shared_imports'; +import { RuntimeType, RuntimeField, SerializedFieldFormat } from '../../shared_imports'; import { useFieldEditorContext } from '../field_editor_context'; type From = 'cluster' | 'custom'; @@ -47,7 +47,7 @@ interface Params { index: string | null; type: RuntimeType | null; script: Required['script'] | null; - format: FieldFormatConfig | null; + format: SerializedFieldFormat | null; document: EsDocument | null; } diff --git a/src/plugins/index_pattern_field_editor/public/shared_imports.ts b/src/plugins/index_pattern_field_editor/public/shared_imports.ts index 7efd029d8c304..4bf895879e763 100644 --- a/src/plugins/index_pattern_field_editor/public/shared_imports.ts +++ b/src/plugins/index_pattern_field_editor/public/shared_imports.ts @@ -19,6 +19,8 @@ export { RuntimeCompositeWithSubFields, } from '../../data/common'; +export { SerializedFieldFormat } from '../../expressions/public'; + export { createKibanaReactContext, toMountPoint, CodeEditor } from '../../kibana_react/public'; export { FieldFormat } from '../../field_formats/common'; diff --git a/src/plugins/index_pattern_field_editor/public/types.ts b/src/plugins/index_pattern_field_editor/public/types.ts index 06ca1bed21726..ed0f149d69192 100644 --- a/src/plugins/index_pattern_field_editor/public/types.ts +++ b/src/plugins/index_pattern_field_editor/public/types.ts @@ -46,8 +46,6 @@ export interface Field extends EnhancedRuntimeField { name: string; } -export type FieldFormatConfig = EnhancedRuntimeField['format']; - export type CloseEditor = () => void; export type FieldPreviewContext = From d246459dacc7017528bcaf0a90be17613dc48305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 31 Aug 2021 12:14:32 +0100 Subject: [PATCH 35/60] Export all publicly accessible interfaces --- api_docs/index_pattern_field_editor.json | 230 +++++++++++++++++- api_docs/index_pattern_field_editor.mdx | 2 +- .../field_format_editor/format_editor.tsx | 2 +- .../components/field_format_editor/index.ts | 2 + .../public/index.ts | 8 +- 5 files changed, 232 insertions(+), 12 deletions(-) diff --git a/api_docs/index_pattern_field_editor.json b/api_docs/index_pattern_field_editor.json index 25cb2cb1d6ea9..e35d8685c4059 100644 --- a/api_docs/index_pattern_field_editor.json +++ b/api_docs/index_pattern_field_editor.json @@ -26,7 +26,13 @@ "text": "FormatEditorProps" }, "

, ", - "FormatEditorState", + { + "pluginId": "indexPatternFieldEditor", + "scope": "public", + "docId": "kibIndexPatternFieldEditorPluginApi", + "section": "def-public.FormatEditorState", + "text": "FormatEditorState" + }, " & S, any>" ], "path": "src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.tsx", @@ -50,7 +56,13 @@ "label": "state", "description": [], "signature": [ - "FormatEditorState", + { + "pluginId": "indexPatternFieldEditor", + "scope": "public", + "docId": "kibIndexPatternFieldEditorPluginApi", + "section": "def-public.FormatEditorState", + "text": "FormatEditorState" + }, " & S" ], "path": "src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.tsx", @@ -73,9 +85,21 @@ "text": "FormatEditorProps" }, "<{}>, state: ", - "FormatEditorState", + { + "pluginId": "indexPatternFieldEditor", + "scope": "public", + "docId": "kibIndexPatternFieldEditorPluginApi", + "section": "def-public.FormatEditorState", + "text": "FormatEditorState" + }, ") => { error: string | undefined; samples: ", - "Sample", + { + "pluginId": "indexPatternFieldEditor", + "scope": "public", + "docId": "kibIndexPatternFieldEditorPluginApi", + "section": "def-public.Sample", + "text": "Sample" + }, "[]; }" ], "path": "src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.tsx", @@ -110,7 +134,13 @@ "label": "state", "description": [], "signature": [ - "FormatEditorState" + { + "pluginId": "indexPatternFieldEditor", + "scope": "public", + "docId": "kibIndexPatternFieldEditorPluginApi", + "section": "def-public.FormatEditorState", + "text": "FormatEditorState" + } ], "path": "src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.tsx", "deprecated": false, @@ -307,6 +337,53 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "indexPatternFieldEditor", + "id": "def-public.FormatEditorState", + "type": "Interface", + "tags": [], + "label": "FormatEditorState", + "description": [], + "path": "src/plugins/index_pattern_field_editor/public/components/field_format_editor/format_editor.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "indexPatternFieldEditor", + "id": "def-public.FormatEditorState.EditorComponent", + "type": "CompoundType", + "tags": [], + "label": "EditorComponent", + "description": [], + "signature": [ + "React.LazyExoticComponent<", + { + "pluginId": "indexPatternFieldEditor", + "scope": "public", + "docId": "kibIndexPatternFieldEditorPluginApi", + "section": "def-public.FieldFormatEditor", + "text": "FieldFormatEditor" + }, + "<{}>> | null" + ], + "path": "src/plugins/index_pattern_field_editor/public/components/field_format_editor/format_editor.tsx", + "deprecated": false + }, + { + "parentPluginId": "indexPatternFieldEditor", + "id": "def-public.FormatEditorState.fieldFormatId", + "type": "string", + "tags": [], + "label": "fieldFormatId", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/index_pattern_field_editor/public/components/field_format_editor/format_editor.tsx", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "indexPatternFieldEditor", "id": "def-public.OpenFieldDeleteModalOptions", @@ -431,7 +508,7 @@ "section": "def-common.IndexPatternField", "text": "IndexPatternField" }, - ") => void) | undefined" + "[]) => void) | undefined" ], "path": "src/plugins/index_pattern_field_editor/public/open_editor.tsx", "deprecated": false, @@ -439,7 +516,7 @@ { "parentPluginId": "indexPatternFieldEditor", "id": "def-public.OpenFieldEditorOptions.onSave.$1", - "type": "Object", + "type": "Array", "tags": [], "label": "field", "description": [], @@ -450,7 +527,8 @@ "docId": "kibDataIndexPatternsPluginApi", "section": "def-common.IndexPatternField", "text": "IndexPatternField" - } + }, + "[]" ], "path": "src/plugins/index_pattern_field_editor/public/open_editor.tsx", "deprecated": false, @@ -474,6 +552,134 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "indexPatternFieldEditor", + "id": "def-public.Props", + "type": "Interface", + "tags": [], + "label": "Props", + "description": [], + "path": "src/plugins/index_pattern_field_editor/public/components/delete_field_provider.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "indexPatternFieldEditor", + "id": "def-public.Props.children", + "type": "Function", + "tags": [], + "label": "children", + "description": [], + "signature": [ + "(deleteFieldHandler: DeleteFieldFunc) => React.ReactNode" + ], + "path": "src/plugins/index_pattern_field_editor/public/components/delete_field_provider.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "indexPatternFieldEditor", + "id": "def-public.Props.children.$1", + "type": "Function", + "tags": [], + "label": "deleteFieldHandler", + "description": [], + "signature": [ + "DeleteFieldFunc" + ], + "path": "src/plugins/index_pattern_field_editor/public/components/delete_field_provider.tsx", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "indexPatternFieldEditor", + "id": "def-public.Props.indexPattern", + "type": "Object", + "tags": [], + "label": "indexPattern", + "description": [], + "signature": [ + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataIndexPatternsPluginApi", + "section": "def-common.IndexPattern", + "text": "IndexPattern" + } + ], + "path": "src/plugins/index_pattern_field_editor/public/components/delete_field_provider.tsx", + "deprecated": false + }, + { + "parentPluginId": "indexPatternFieldEditor", + "id": "def-public.Props.onDelete", + "type": "Function", + "tags": [], + "label": "onDelete", + "description": [], + "signature": [ + "((fieldNames: string[]) => void) | undefined" + ], + "path": "src/plugins/index_pattern_field_editor/public/components/delete_field_provider.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "indexPatternFieldEditor", + "id": "def-public.Props.onDelete.$1", + "type": "Array", + "tags": [], + "label": "fieldNames", + "description": [], + "signature": [ + "string[]" + ], + "path": "src/plugins/index_pattern_field_editor/public/components/delete_field_provider.tsx", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "indexPatternFieldEditor", + "id": "def-public.Sample", + "type": "Interface", + "tags": [], + "label": "Sample", + "description": [], + "path": "src/plugins/index_pattern_field_editor/public/components/field_format_editor/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "indexPatternFieldEditor", + "id": "def-public.Sample.input", + "type": "CompoundType", + "tags": [], + "label": "input", + "description": [], + "signature": [ + "string | number | React.ReactText[] | Record" + ], + "path": "src/plugins/index_pattern_field_editor/public/components/field_format_editor/types.ts", + "deprecated": false + }, + { + "parentPluginId": "indexPatternFieldEditor", + "id": "def-public.Sample.output", + "type": "string", + "tags": [], + "label": "output", + "description": [], + "path": "src/plugins/index_pattern_field_editor/public/components/field_format_editor/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false } ], "enums": [], @@ -722,7 +928,13 @@ "description": [], "signature": [ "React.FunctionComponent<", - "Props", + { + "pluginId": "indexPatternFieldEditor", + "scope": "public", + "docId": "kibIndexPatternFieldEditorPluginApi", + "section": "def-public.Props", + "text": "Props" + }, ">" ], "path": "src/plugins/index_pattern_field_editor/public/types.ts", diff --git a/api_docs/index_pattern_field_editor.mdx b/api_docs/index_pattern_field_editor.mdx index 7a3cfd0e66bbe..bdf963a71433d 100644 --- a/api_docs/index_pattern_field_editor.mdx +++ b/api_docs/index_pattern_field_editor.mdx @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 42 | 2 | 39 | 3 | +| 54 | 2 | 51 | 0 | ## Client diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/format_editor.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/format_editor.tsx index c6f5fc9899ac7..3ea9dcf3dbd5a 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/format_editor.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/format_editor.tsx @@ -22,7 +22,7 @@ export interface FormatEditorProps { onError: (error?: string) => void; } -interface FormatEditorState { +export interface FormatEditorState { EditorComponent: LazyExoticComponent | null; fieldFormatId?: string; } diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/index.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/index.ts index 34619f53e9eed..d0f41c8c35fbe 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/index.ts +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/index.ts @@ -7,4 +7,6 @@ */ export { FormatSelectEditor, FormatSelectEditorProps } from './field_format_editor'; +export type { FormatEditorState } from './format_editor'; +export type { Sample } from './types'; export * from './editors'; diff --git a/src/plugins/index_pattern_field_editor/public/index.ts b/src/plugins/index_pattern_field_editor/public/index.ts index 6546dabcb2c44..a31ae03d7df49 100644 --- a/src/plugins/index_pattern_field_editor/public/index.ts +++ b/src/plugins/index_pattern_field_editor/public/index.ts @@ -25,7 +25,13 @@ export type { PluginStart as IndexPatternFieldEditorStart, } from './types'; export { DefaultFormatEditor } from './components/field_format_editor/editors/default/default'; -export { FieldFormatEditorFactory, FieldFormatEditor, FormatEditorProps } from './components'; +export { FieldFormatEditorFactory, FieldFormatEditor } from './components'; +export type { + DeleteFieldProviderProps, + FormatEditorProps, + FormatEditorState, + Sample, +} from './components'; export function plugin() { return new IndexPatternFieldEditorPlugin(); From 4894c294571801c14bb02394d3128308520214e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 31 Aug 2021 13:30:35 +0100 Subject: [PATCH 36/60] Add dependency on the "expressions" plugin --- src/plugins/index_pattern_field_editor/kibana.json | 2 +- src/plugins/index_pattern_field_editor/public/shared_imports.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/index_pattern_field_editor/kibana.json b/src/plugins/index_pattern_field_editor/kibana.json index 898e7c564e57f..6fff99dfd6b6a 100644 --- a/src/plugins/index_pattern_field_editor/kibana.json +++ b/src/plugins/index_pattern_field_editor/kibana.json @@ -5,7 +5,7 @@ "ui": true, "requiredPlugins": ["data"], "optionalPlugins": ["usageCollection"], - "requiredBundles": ["kibanaReact", "esUiShared", "usageCollection", "fieldFormats"], + "requiredBundles": ["kibanaReact", "esUiShared", "usageCollection", "fieldFormats", "expressions"], "owner": { "name": "App Services", "githubTeam": "kibana-app-services" diff --git a/src/plugins/index_pattern_field_editor/public/shared_imports.ts b/src/plugins/index_pattern_field_editor/public/shared_imports.ts index 4bf895879e763..bb332a7c0ded3 100644 --- a/src/plugins/index_pattern_field_editor/public/shared_imports.ts +++ b/src/plugins/index_pattern_field_editor/public/shared_imports.ts @@ -19,7 +19,7 @@ export { RuntimeCompositeWithSubFields, } from '../../data/common'; -export { SerializedFieldFormat } from '../../expressions/public'; +export type { SerializedFieldFormat } from '../../expressions/public'; export { createKibanaReactContext, toMountPoint, CodeEditor } from '../../kibana_react/public'; From 113a5f3b3af94337ddfde19d2758f51458175bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 31 Aug 2021 14:44:08 +0100 Subject: [PATCH 37/60] Set dep on expressions on requiredPlugins and not requiredBundles --- src/plugins/index_pattern_field_editor/kibana.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/index_pattern_field_editor/kibana.json b/src/plugins/index_pattern_field_editor/kibana.json index 6fff99dfd6b6a..7275344f04286 100644 --- a/src/plugins/index_pattern_field_editor/kibana.json +++ b/src/plugins/index_pattern_field_editor/kibana.json @@ -3,9 +3,9 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["data"], + "requiredPlugins": ["data", "expressions"], "optionalPlugins": ["usageCollection"], - "requiredBundles": ["kibanaReact", "esUiShared", "usageCollection", "fieldFormats", "expressions"], + "requiredBundles": ["kibanaReact", "esUiShared", "usageCollection", "fieldFormats"], "owner": { "name": "App Services", "githubTeam": "kibana-app-services" From 8fb592a9e5bbb8479778972005086289ab4b19c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 31 Aug 2021 15:24:07 +0100 Subject: [PATCH 38/60] Revert type change in ml plugin --- .../ml/public/application/components/data_grid/common.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/ml/public/application/components/data_grid/common.ts b/x-pack/plugins/ml/public/application/components/data_grid/common.ts index 8690720814e96..a64594e86a757 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/common.ts +++ b/x-pack/plugins/ml/public/application/components/data_grid/common.ts @@ -6,6 +6,7 @@ */ import moment from 'moment-timezone'; +import { estypes } from '@elastic/elasticsearch'; import { useEffect, useMemo } from 'react'; import { @@ -23,7 +24,6 @@ import { IFieldType, ES_FIELD_TYPES, KBN_FIELD_TYPES, - RuntimeType, } from '../../../../../../../src/plugins/data/public'; import { DEFAULT_RESULTS_FIELD } from '../../../../common/constants/data_frame_analytics'; @@ -181,7 +181,7 @@ export const getDataGridSchemasFromFieldTypes = (fieldTypes: FieldTypes, results export const NON_AGGREGATABLE = 'non-aggregatable'; export const getDataGridSchemaFromESFieldType = ( - fieldType: ES_FIELD_TYPES | undefined | RuntimeType + fieldType: ES_FIELD_TYPES | undefined | estypes.MappingRuntimeField['type'] ): string | undefined => { // Built-in values are ['boolean', 'currency', 'datetime', 'numeric', 'json'] // To fall back to the default string schema it needs to be undefined. From 7aae1e7f6f5aadae985187fd9d03b02d34c47c42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 31 Aug 2021 16:49:13 +0100 Subject: [PATCH 39/60] Fix failing functional test (ml plugin) --- .../index_data_visualizer/data_loader/data_loader.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts index 1b92eaddd1343..9f5b85c0f502c 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts @@ -21,7 +21,6 @@ const MAX_EXAMPLES_DEFAULT: number = 10; export class DataLoader { private _indexPattern: IndexPattern; - private _runtimeMappings: estypes.MappingRuntimeFields; private _indexPatternTitle: IndexPatternTitle = ''; private _maxExamples: number = MAX_EXAMPLES_DEFAULT; private _toastNotifications: CoreSetup['notifications']['toasts']; @@ -31,8 +30,6 @@ export class DataLoader { toastNotifications: CoreSetup['notifications']['toasts'] ) { this._indexPattern = indexPattern; - this._runtimeMappings = this._indexPattern.getComputedFields() - .runtimeFields as estypes.MappingRuntimeFields; this._indexPatternTitle = indexPattern.title; this._toastNotifications = toastNotifications; } @@ -70,7 +67,8 @@ export class DataLoader { latest, aggregatableFields, nonAggregatableFields, - runtimeMappings: this._runtimeMappings, + runtimeMappings: this._indexPattern.getComputedFields() + .runtimeFields as estypes.MappingRuntimeFields, }); return stats; @@ -94,7 +92,8 @@ export class DataLoader { interval, fields, maxExamples: this._maxExamples, - runtimeMappings: this._runtimeMappings, + runtimeMappings: this._indexPattern.getComputedFields() + .runtimeFields as estypes.MappingRuntimeFields, }); return stats; From ca81d0be966f34269292092f507b46a0e236cddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 31 Aug 2021 16:54:40 +0100 Subject: [PATCH 40/60] Fix old "object" type with "composite" type --- .../public/components/field_editor_flyout_content_container.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index 3e00720492c87..06011ffda2726 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -140,7 +140,7 @@ export const FieldEditorFlyoutContentContainer = ({ const saveRuntimeField = useCallback( (updatedField: Field): [IndexPatternField] => { - if (field?.type !== undefined && field?.type === 'object') { + if (field?.type !== undefined && field?.type === 'composite') { // A previous runtime composite is now a runtime field indexPattern.removeRuntimeComposite(field.name); } else if (field?.name && field.name !== updatedField.name) { From cd62b78000342ab45b8693023f6c63c9f5d15732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 31 Aug 2021 17:45:33 +0100 Subject: [PATCH 41/60] Fix TS issue --- .../common/index_patterns/index_patterns/index_pattern.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 1cfde72539102..cf95099b03d55 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -386,14 +386,14 @@ export class DataView implements IIndexPattern { * @param name Field name * @param enhancedRuntimeField Runtime field definition */ - addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): IndexPatternField { + addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): DataViewField { const { type, script, parentComposite, customLabel, format, popularity } = enhancedRuntimeField; const runtimeField: RuntimeField = { type, script, parentComposite }; this.runtimeFieldMap[name] = runtimeField; // Create the field if it does not exist or update an existing one - let createdField: IndexPatternField | undefined; + let createdField: DataViewField | undefined; const existingField = this.getFieldByName(name); if (existingField) { existingField.runtimeField = runtimeField; @@ -484,7 +484,7 @@ export class DataView implements IIndexPattern { addRuntimeComposite( name: string, runtimeComposite: RuntimeCompositeWithSubFields - ): IndexPatternField[] { + ): DataViewField[] { if (!runtimeComposite.subFields || Object.keys(runtimeComposite.subFields).length === 0) { throw new Error(`Can't save runtime composite [name = ${name}] without subfields.`); } @@ -495,7 +495,7 @@ export class DataView implements IIndexPattern { const { script, subFields } = runtimeComposite; - const fieldsCreated: IndexPatternField[] = []; + const fieldsCreated: DataViewField[] = []; for (const [subFieldName, subField] of Object.entries(subFields)) { const field = this.addRuntimeField(`${name}.${subFieldName}`, { From e80f385b5383d062f30451929b27575be7ed7388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 31 Aug 2021 18:38:07 +0100 Subject: [PATCH 42/60] Fix TS issue --- x-pack/plugins/transform/public/app/hooks/use_index_data.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts index 55a304207a1c7..c189642ccb691 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts @@ -117,6 +117,7 @@ export const useIndexData = ( if (combinedRuntimeMappings !== undefined) { result = Object.keys(combinedRuntimeMappings).map((fieldName) => { const field = combinedRuntimeMappings[fieldName]; + // @ts-expect-error @elastic/elasticsearch does not support yet "composite" type for runtime fields const schema = getDataGridSchemaFromESFieldType(field.type); return { id: fieldName, schema }; }); From 38a96287d21c5deda9abc8a3fad207656c56eae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Wed, 1 Sep 2021 11:36:12 +0100 Subject: [PATCH 43/60] Add validation to not allow shadowing runtime composite with runtime field --- .../index_patterns/index_pattern.ts | 27 ++++++++++++++++--- .../public/components/field_editor/lib.ts | 27 ++++++++++++++----- .../components/field_editor_context.tsx | 5 +++- .../field_editor_flyout_content_container.tsx | 19 ++++++++----- 4 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index cf95099b03d55..20e297297465a 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -387,6 +387,14 @@ export class DataView implements IIndexPattern { * @param enhancedRuntimeField Runtime field definition */ addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): DataViewField { + const existRuntimeCompositeWithSameName = this.getRuntimeComposite(name) !== null; + + if (existRuntimeCompositeWithSameName) { + throw new Error( + `Can't add runtime field ["${name}"] as there is already a runtime composite with the same name.` + ); + } + const { type, script, parentComposite, customLabel, format, popularity } = enhancedRuntimeField; const runtimeField: RuntimeField = { type, script, parentComposite }; @@ -460,9 +468,13 @@ export class DataView implements IIndexPattern { const existingField = this.getFieldByName(name); if (existingField) { - if (existingField.runtimeField?.parentComposite !== undefined) { + const parentCompositeName = existingField.runtimeField?.parentComposite; + const hasParentComposite = + parentCompositeName !== undefined && + this.getRuntimeComposite(parentCompositeName!) !== null; + if (hasParentComposite) { throw new Error( - `Can't remove runtime field ["${name}"] as it belongs to the composite runtime ["${existingField.runtimeField.parentComposite}"]` + `Can't remove runtime field ["${name}"] as it belongs to the composite runtime ["${parentCompositeName}"]` ); } @@ -562,6 +574,13 @@ export class DataView implements IIndexPattern { }; } + /** + * Return all the runtime composite fields + */ + getAllRuntimeComposites(): Record { + return _.cloneDeep(this.runtimeCompositeMap); + } + /** * Remove a runtime composite with its associated subFields * @param name - Runtime composite name to remove @@ -570,12 +589,12 @@ export class DataView implements IIndexPattern { const existingRuntimeComposite = this.getRuntimeComposite(name); if (!!existingRuntimeComposite) { + delete this.runtimeCompositeMap[name]; + // Remove all subFields for (const subFieldName of existingRuntimeComposite.subFields) { this.removeRuntimeField(`${name}.${subFieldName}`); } - - delete this.runtimeCompositeMap[name]; } } diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/lib.ts b/src/plugins/index_pattern_field_editor/public/components/field_editor/lib.ts index ba44682ba65e0..44455d958a7cd 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor/lib.ts +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/lib.ts @@ -9,14 +9,15 @@ import { i18n } from '@kbn/i18n'; import { ValidationFunc, FieldConfig } from '../../shared_imports'; -import { Field } from '../../types'; +import type { Field } from '../../types'; +import type { Context } from '../field_editor_context'; import { schema } from './form_schema'; import type { Props } from './field_editor'; const createNameNotAllowedValidator = ( - namesNotAllowed: string[] + namesNotAllowed: Context['namesNotAllowed'] ): ValidationFunc<{}, string, string> => ({ value }) => { - if (namesNotAllowed.includes(value)) { + if (namesNotAllowed.fields.includes(value)) { return { message: i18n.translate( 'indexPatternFieldEditor.editor.runtimeFieldsEditor.existRuntimeFieldNamesValidationErrorMessage', @@ -25,6 +26,15 @@ const createNameNotAllowedValidator = ( } ), }; + } else if (namesNotAllowed.runtimeComposites.includes(value)) { + return { + message: i18n.translate( + 'indexPatternFieldEditor.editor.runtimeFieldsEditor.existCompositeNamesValidationErrorMessage', + { + defaultMessage: 'A runtime composite with this name already exists.', + } + ), + }; } }; @@ -36,7 +46,7 @@ const createNameNotAllowedValidator = ( * @param field Initial value of the form */ export const getNameFieldConfig = ( - namesNotAllowed?: string[], + namesNotAllowed?: Context['namesNotAllowed'], field?: Props['field'] ): FieldConfig => { const nameFieldConfig = schema.name as FieldConfig; @@ -45,15 +55,18 @@ export const getNameFieldConfig = ( return nameFieldConfig; } + const filterOutCurrentFieldName = (name: string) => name !== field?.name; + // Add validation to not allow duplicates return { ...nameFieldConfig!, validations: [ ...(nameFieldConfig.validations ?? []), { - validator: createNameNotAllowedValidator( - namesNotAllowed.filter((name) => name !== field?.name) - ), + validator: createNameNotAllowedValidator({ + fields: namesNotAllowed.fields.filter(filterOutCurrentFieldName), + runtimeComposites: namesNotAllowed.runtimeComposites.filter(filterOutCurrentFieldName), + }), }, ], }; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_context.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_context.tsx index 74bf2657ba3de..88f9352465133 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_context.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_context.tsx @@ -31,7 +31,10 @@ export interface Context { * e.g we probably don't want a user to give a name of an existing * runtime field (for that the user should edit the existing runtime field). */ - namesNotAllowed: string[]; + namesNotAllowed: { + fields: string[]; + runtimeComposites: string[]; + }; /** * An array of existing concrete fields. If the user gives a name to the runtime * field that matches one of the concrete fields, a callout will be displayed diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index 06011ffda2726..42b4f20da5397 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -83,7 +83,14 @@ export const FieldEditorFlyoutContentContainer = ({ const { fields } = indexPattern; - const namesNotAllowed = useMemo(() => fields.map((fld) => fld.name), [fields]); + const namesNotAllowed = useMemo(() => { + const fieldNames = indexPattern.fields.map((fld) => fld.name); + const runtimeCompositeNames = Object.keys(indexPattern.getAllRuntimeComposites()); + return { + fields: fieldNames, + runtimeComposites: runtimeCompositeNames, + }; + }, [indexPattern]); const existingConcreteFields = useMemo(() => { const existing: Array<{ name: string; type: string }> = []; @@ -201,12 +208,12 @@ export const FieldEditorFlyoutContentContainer = ({ setIsSaving(true); - const editedFields: IndexPatternField[] = - fieldTypeToProcess === 'runtime' - ? updateRuntimeField(updatedField) - : updateConcreteField(updatedField as Field); - try { + const editedFields: DataViewField[] = + fieldTypeToProcess === 'runtime' + ? updateRuntimeField(updatedField) + : updateConcreteField(updatedField as Field); + await indexPatternService.updateSavedObject(indexPattern); const message = i18n.translate('indexPatternFieldEditor.deleteField.savedHeader', { From 62302d4cf7084d0c4a9396b0cbbe90292cf68380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Wed, 1 Sep 2021 11:36:45 +0100 Subject: [PATCH 44/60] Use the new "DataView" and "DataViewField" interfaces --- .../index_pattern_field_editor/README.md | 8 ++++---- .../components/delete_field_provider.tsx | 4 ++-- .../components/field_editor_context.tsx | 4 ++-- .../field_editor_flyout_content_container.tsx | 20 +++++++++---------- .../field_format_editor.tsx | 4 ++-- .../public/lib/remove_fields.ts | 4 ++-- .../public/lib/serialization.ts | 6 +++--- .../public/open_delete_modal.tsx | 4 ++-- .../public/open_editor.tsx | 10 +++++----- .../public/plugin.test.tsx | 4 ++-- .../public/shared_imports.ts | 3 ++- 11 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/plugins/index_pattern_field_editor/README.md b/src/plugins/index_pattern_field_editor/README.md index f6ef06cd77b36..1427643858af5 100644 --- a/src/plugins/index_pattern_field_editor/README.md +++ b/src/plugins/index_pattern_field_editor/README.md @@ -27,9 +27,9 @@ Use this method to open the index pattern field editor to either create (runtime This is the only required option. You need to provide the context in which the editor is being consumed. This object has the following properties: -- `indexPattern: IndexPattern`: the index pattern you want to create/edit the field into. +- `indexPattern: DataView`: the index pattern you want to create/edit the field into. -`onSave(field: IndexPatternField): void` (optional) +`onSave(field: DataViewField): void` (optional) You can provide an optional `onSave` handler to be notified when the field has being created/updated. This handler is called after the field has been persisted to the saved object. @@ -47,7 +47,7 @@ Use this method to open a confirmation modal to delete runtime fields from an in You need to provide the context in which the deletion modal is being consumed. This object has the following properties: -- `indexPattern: IndexPattern`: the index pattern you want to delete fields from. +- `indexPattern: DataView`: the index pattern you want to delete fields from. `onDelete(fieldNames: string[]): void` (optional) @@ -63,7 +63,7 @@ This children func React component provides a handler to delete one or multiple #### Props -* `indexPattern: IndexPattern`: the current index pattern. (**required**) +* `indexPattern: DataView`: the current index pattern. (**required**) ```js diff --git a/src/plugins/index_pattern_field_editor/public/components/delete_field_provider.tsx b/src/plugins/index_pattern_field_editor/public/components/delete_field_provider.tsx index 4ec766fe1c851..f809d0d4bad0e 100644 --- a/src/plugins/index_pattern_field_editor/public/components/delete_field_provider.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/delete_field_provider.tsx @@ -8,14 +8,14 @@ import React, { useCallback, useRef, useEffect } from 'react'; -import { IndexPattern } from '../shared_imports'; +import { DataView } from '../shared_imports'; import { OpenFieldDeleteModalOptions } from '../open_delete_modal'; import { CloseEditor } from '../types'; type DeleteFieldFunc = (fieldName: string | string[]) => void; export interface Props { children: (deleteFieldHandler: DeleteFieldFunc) => React.ReactNode; - indexPattern: IndexPattern; + indexPattern: DataView; onDelete?: (fieldNames: string[]) => void; } diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_context.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_context.tsx index 88f9352465133..f2ef205a32d14 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_context.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_context.tsx @@ -8,12 +8,12 @@ import React, { createContext, useContext, FunctionComponent, useMemo } from 'react'; import { NotificationsStart, CoreStart } from 'src/core/public'; -import type { IndexPattern, DataPublicPluginStart } from '../shared_imports'; +import type { DataView, DataPublicPluginStart } from '../shared_imports'; import { ApiService } from '../lib/api'; import type { InternalFieldType, PluginStart } from '../types'; export interface Context { - indexPattern: IndexPattern; + indexPattern: DataView; fieldTypeToProcess: InternalFieldType; uiSettings: CoreStart['uiSettings']; links: { diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index 42b4f20da5397..92f2bda29a5ee 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -12,8 +12,8 @@ import { i18n } from '@kbn/i18n'; import { METRIC_TYPE } from '@kbn/analytics'; import { - IndexPatternField, - IndexPattern, + DataViewField, + DataView, DataPublicPluginStart, UsageCollectionStart, RuntimeCompositeWithSubFields, @@ -30,18 +30,18 @@ import { FieldPreviewProvider } from './preview'; export interface Props { /** Handler for the "save" footer button */ - onSave: (field: IndexPatternField[]) => void; + onSave: (field: DataViewField[]) => void; /** Handler for the "cancel" footer button */ onCancel: () => void; onMounted?: FieldEditorFlyoutContentProps['onMounted']; /** The docLinks start service from core */ docLinks: DocLinksStart; - /** The index pattern where the field will be added */ - indexPattern: IndexPattern; + /** The data view where the field will be added */ + indexPattern: DataView; /** The Kibana field type of the field to create or edit (default: "runtime") */ fieldTypeToProcess: InternalFieldType; /** Optional field to edit */ - field?: IndexPatternField; + field?: DataViewField; /** Services */ indexPatternService: DataPublicPluginStart['indexPatterns']; notifications: NotificationsStart; @@ -125,7 +125,7 @@ export const FieldEditorFlyoutContentContainer = ({ ); const saveCompositeRuntime = useCallback( - (updatedField: RuntimeCompositeWithSubFields): IndexPatternField[] => { + (updatedField: RuntimeCompositeWithSubFields): DataViewField[] => { if (field?.type !== undefined && field?.type !== 'composite') { // A previous runtime field is now a runtime composite indexPattern.removeRuntimeField(field.name); @@ -146,7 +146,7 @@ export const FieldEditorFlyoutContentContainer = ({ ); const saveRuntimeField = useCallback( - (updatedField: Field): [IndexPatternField] => { + (updatedField: Field): [DataViewField] => { if (field?.type !== undefined && field?.type === 'composite') { // A previous runtime composite is now a runtime field indexPattern.removeRuntimeComposite(field.name); @@ -162,7 +162,7 @@ export const FieldEditorFlyoutContentContainer = ({ ); const updateRuntimeField = useCallback( - (updatedField: Field | RuntimeCompositeWithSubFields): IndexPatternField[] => { + (updatedField: Field | RuntimeCompositeWithSubFields): DataViewField[] => { return (updatedField as RuntimeCompositeWithSubFields).subFields !== undefined ? saveCompositeRuntime(updatedField as RuntimeCompositeWithSubFields) : saveRuntimeField(updatedField as Field); @@ -171,7 +171,7 @@ export const FieldEditorFlyoutContentContainer = ({ ); const updateConcreteField = useCallback( - (updatedField: Field): IndexPatternField[] => { + (updatedField: Field): DataViewField[] => { const editedField = indexPattern.getFieldByName(updatedField.name); if (!editedField) { diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx index bbd9fe88f818f..af16f178f3e68 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx @@ -11,7 +11,7 @@ import { EuiCode, EuiFormRow, EuiSelect } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { - IndexPattern, + DataView, KBN_FIELD_TYPES, ES_FIELD_TYPES, DataPublicPluginStart, @@ -26,7 +26,7 @@ import { FormatEditor } from './format_editor'; export interface FormatSelectEditorProps { esTypes: ES_FIELD_TYPES[]; - indexPattern: IndexPattern; + indexPattern: DataView; fieldFormatEditors: FormatEditorServiceStart['fieldFormatEditors']; fieldFormats: DataPublicPluginStart['fieldFormats']; uiSettings: CoreStart['uiSettings']; diff --git a/src/plugins/index_pattern_field_editor/public/lib/remove_fields.ts b/src/plugins/index_pattern_field_editor/public/lib/remove_fields.ts index bc9457424099f..43ea99a761ff5 100644 --- a/src/plugins/index_pattern_field_editor/public/lib/remove_fields.ts +++ b/src/plugins/index_pattern_field_editor/public/lib/remove_fields.ts @@ -9,13 +9,13 @@ import { i18n } from '@kbn/i18n'; import { METRIC_TYPE } from '@kbn/analytics'; import { NotificationsStart } from 'src/core/public'; -import { IndexPattern, UsageCollectionStart } from '../shared_imports'; +import { DataView, UsageCollectionStart } from '../shared_imports'; import { pluginName } from '../constants'; import { DataPublicPluginStart } from '../../../data/public'; export async function removeFields( fieldNames: string[], - indexPattern: IndexPattern, + indexPattern: DataView, services: { indexPatternService: DataPublicPluginStart['indexPatterns']; usageCollection: UsageCollectionStart; diff --git a/src/plugins/index_pattern_field_editor/public/lib/serialization.ts b/src/plugins/index_pattern_field_editor/public/lib/serialization.ts index 8400138d12d39..2c024cef8f9f2 100644 --- a/src/plugins/index_pattern_field_editor/public/lib/serialization.ts +++ b/src/plugins/index_pattern_field_editor/public/lib/serialization.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import { IndexPatternField, IndexPattern, RuntimeType } from '../shared_imports'; +import { DataViewField, DataView, RuntimeType } from '../shared_imports'; import type { Field } from '../types'; export const deserializeField = ( - indexPattern: IndexPattern, - field?: IndexPatternField + indexPattern: DataView, + field?: DataViewField ): Field | undefined => { if (field === undefined) { return undefined; diff --git a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx index 2620f65d2d568..2b54c11692b32 100644 --- a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx @@ -12,7 +12,7 @@ import { CoreStart, OverlayRef } from 'src/core/public'; import { toMountPoint, DataPublicPluginStart, - IndexPattern, + DataView, UsageCollectionStart, } from './shared_imports'; @@ -23,7 +23,7 @@ import { removeFields } from './lib/remove_fields'; export interface OpenFieldDeleteModalOptions { ctx: { - indexPattern: IndexPattern; + indexPattern: DataView; }; onDelete?: (fieldNames: string[]) => void; fieldName: string | string[]; diff --git a/src/plugins/index_pattern_field_editor/public/open_editor.tsx b/src/plugins/index_pattern_field_editor/public/open_editor.tsx index a67c4361178c2..d436acc4a7bc9 100644 --- a/src/plugins/index_pattern_field_editor/public/open_editor.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_editor.tsx @@ -13,9 +13,9 @@ import { i18n } from '@kbn/i18n'; import { createKibanaReactContext, toMountPoint, - IndexPatternField, + DataViewField, DataPublicPluginStart, - IndexPattern, + DataView, UsageCollectionStart, } from './shared_imports'; @@ -26,9 +26,9 @@ import { FieldEditorLoader } from './components/field_editor_loader'; export interface OpenFieldEditorOptions { ctx: { - indexPattern: IndexPattern; + indexPattern: DataView; }; - onSave?: (field: IndexPatternField[]) => void; + onSave?: (field: DataViewField[]) => void; fieldName?: string; } @@ -80,7 +80,7 @@ export const getFieldEditorOpener = ({ } }; - const onSaveField = (updatedField: IndexPatternField[]) => { + const onSaveField = (updatedField: DataViewField[]) => { closeEditor(); if (onSave) { diff --git a/src/plugins/index_pattern_field_editor/public/plugin.test.tsx b/src/plugins/index_pattern_field_editor/public/plugin.test.tsx index 75bb1322d305e..eb8a65131a631 100644 --- a/src/plugins/index_pattern_field_editor/public/plugin.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/plugin.test.tsx @@ -25,7 +25,7 @@ import { usageCollectionPluginMock } from '../../usage_collection/public/mocks'; import { FieldEditorLoader } from './components/field_editor_loader'; import { IndexPatternFieldEditorPlugin } from './plugin'; import { DeleteFieldModal } from './components/confirm_modals/delete_field_modal'; -import { IndexPattern } from './shared_imports'; +import { DataView } from './shared_imports'; const noop = () => {}; @@ -112,7 +112,7 @@ describe('IndexPatternFieldEditorPlugin', () => { }; const { openDeleteModal } = await plugin.start(coreStartMocked, pluginStartMocked); - const indexPatternMock = ({ removeRuntimeField: removeFieldSpy } as unknown) as IndexPattern; + const indexPatternMock = ({ removeRuntimeField: removeFieldSpy } as unknown) as DataView; openDeleteModal({ onDelete: onDeleteSpy, diff --git a/src/plugins/index_pattern_field_editor/public/shared_imports.ts b/src/plugins/index_pattern_field_editor/public/shared_imports.ts index bb332a7c0ded3..e25cf38e57c0d 100644 --- a/src/plugins/index_pattern_field_editor/public/shared_imports.ts +++ b/src/plugins/index_pattern_field_editor/public/shared_imports.ts @@ -6,7 +6,8 @@ * Side Public License, v 1. */ -export { IndexPattern, IndexPatternField, DataPublicPluginStart } from '../../data/public'; +export { DataPublicPluginStart } from '../../data/public'; +export { DataView, DataViewField } from '../../data/common'; export { UsageCollectionStart } from '../../usage_collection/public'; From a8643182d02831295479d6267e87298f762805f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Wed, 1 Sep 2021 11:59:57 +0100 Subject: [PATCH 45/60] Fix TS issues --- .../__jest__/client_integration/field_editor.test.tsx | 10 ++++++++-- .../client_integration/helpers/setup_environment.tsx | 2 +- .../field_format_editor/field_format_editor.tsx | 9 ++------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx index 16803533bf7fd..a7f990bf8259d 100644 --- a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx +++ b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx @@ -128,7 +128,10 @@ describe('', () => { onChange, }, { - namesNotAllowed: existingFields, + namesNotAllowed: { + fields: existingFields, + runtimeComposites: [], + }, existingConcreteFields: [], fieldTypeToProcess: 'runtime', } @@ -165,7 +168,10 @@ describe('', () => { onChange, }, { - namesNotAllowed: existingRuntimeFieldNames, + namesNotAllowed: { + fields: existingRuntimeFieldNames, + runtimeComposites: [], + }, existingConcreteFields: [], fieldTypeToProcess: 'runtime', } diff --git a/src/plugins/index_pattern_field_editor/__jest__/client_integration/helpers/setup_environment.tsx b/src/plugins/index_pattern_field_editor/__jest__/client_integration/helpers/setup_environment.tsx index d87b49d35c68e..cf9a8d5406ac2 100644 --- a/src/plugins/index_pattern_field_editor/__jest__/client_integration/helpers/setup_environment.tsx +++ b/src/plugins/index_pattern_field_editor/__jest__/client_integration/helpers/setup_environment.tsx @@ -94,7 +94,7 @@ export const WithFieldEditorDependencies = Date: Wed, 1 Sep 2021 15:11:13 +0100 Subject: [PATCH 46/60] Add method to return runtime_mappings of a Data view --- .../index_patterns/index_pattern.ts | 21 ++++++++++++------- .../data/common/index_patterns/types.ts | 11 +++++----- .../data_loader/data_loader.ts | 6 ++---- .../lens/server/routes/existing_fields.ts | 15 ++++++------- 4 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 20e297297465a..818b410967f9a 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -202,13 +202,7 @@ export class DataView implements IIndexPattern { }; }); - const runtimeFields: Record = { - ...getRuntimeFieldsFromMap(this.runtimeFieldMap), - ...getRuntimeCompositeFieldsFromMap( - this.runtimeCompositeMap, - this.getRuntimeField.bind(this) - ), - }; + const runtimeFields = this.getRuntimeMappings(); return { storedFields: ['*'], @@ -598,6 +592,19 @@ export class DataView implements IIndexPattern { } } + /** + * Return the "runtime_mappings" section of the ES search query + */ + getRuntimeMappings(): Record { + return { + ...getRuntimeFieldsFromMap(this.runtimeFieldMap), + ...getRuntimeCompositeFieldsFromMap( + this.runtimeCompositeMap, + this.getRuntimeField.bind(this) + ), + }; + } + /** * Get formatter for a given field name. Return undefined if none exists * @param field diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 4e922be622615..44d04fb8f9e72 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -22,10 +22,13 @@ export type RuntimeType = estypes.MappingRuntimeFieldType | 'composite'; /** * The RuntimeField that will be sent in the ES Query "runtime_mappings" object * We extends the object until @elastic/elasticsearch supports "composite" type - * and its "fields" object + * and its "fields" object. + * + * To simplify the consuming code we enforce the script to be `InlineScript` type (and not also `string`) */ -export interface ESRuntimeField extends Omit { +export interface ESRuntimeField extends Omit { type: RuntimeType; + script?: estypes.InlineScript; fields?: Record< string, { @@ -38,10 +41,8 @@ export interface ESRuntimeField extends Omit { - script?: estypes.InlineScript; +export interface RuntimeField extends ESRuntimeField { parentComposite?: string; } diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts index 9f5b85c0f502c..7bce589f3baca 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts @@ -67,8 +67,7 @@ export class DataLoader { latest, aggregatableFields, nonAggregatableFields, - runtimeMappings: this._indexPattern.getComputedFields() - .runtimeFields as estypes.MappingRuntimeFields, + runtimeMappings: this._indexPattern.getRuntimeMappings() as estypes.MappingRuntimeFields, }); return stats; @@ -92,8 +91,7 @@ export class DataLoader { interval, fields, maxExamples: this._maxExamples, - runtimeMappings: this._indexPattern.getComputedFields() - .runtimeFields as estypes.MappingRuntimeFields, + runtimeMappings: this._indexPattern.getRuntimeMappings() as estypes.MappingRuntimeFields, }); return stats; diff --git a/x-pack/plugins/lens/server/routes/existing_fields.ts b/x-pack/plugins/lens/server/routes/existing_fields.ts index a654608f97cc4..ad65b05ba66c2 100644 --- a/x-pack/plugins/lens/server/routes/existing_fields.ts +++ b/x-pack/plugins/lens/server/routes/existing_fields.ts @@ -115,6 +115,8 @@ async function fetchFieldExistence({ const indexPattern = await indexPatternsService.get(indexPatternId); const fields = buildFieldList(indexPattern, metaFields); + const runtimeMappings = indexPattern.getRuntimeMappings(); + const docs = await fetchIndexPatternStats({ fromDate, toDate, @@ -123,6 +125,8 @@ async function fetchFieldExistence({ index: indexPattern.title, timeFieldName: timeFieldName || indexPattern.timeFieldName, fields, + // @ts-expect-error The MappingRuntimeField from @elastic/elasticsearch does not expose the "composite" runtime type yet + runtimeMappings, }); return { @@ -157,6 +161,7 @@ async function fetchIndexPatternStats({ fromDate, toDate, fields, + runtimeMappings, }: { client: ElasticsearchClient; index: string; @@ -165,6 +170,7 @@ async function fetchIndexPatternStats({ fromDate?: string; toDate?: string; fields: Field[]; + runtimeMappings: estypes.MappingRuntimeFields; }) { const filter = timeFieldName && fromDate && toDate @@ -188,7 +194,7 @@ async function fetchIndexPatternStats({ }; const scriptedFields = fields.filter((f) => f.isScript); - const runtimeFields = fields.filter((f) => f.runtimeField); + const { body: result } = await client.search( { index, @@ -199,12 +205,7 @@ async function fetchIndexPatternStats({ sort: timeFieldName && fromDate && toDate ? [{ [timeFieldName]: 'desc' }] : [], fields: ['*'], _source: false, - runtime_mappings: runtimeFields.reduce((acc, field) => { - if (!field.runtimeField) return acc; - // @ts-expect-error The MappingRuntimeField from @elastic/elasticsearch does not expose the "composite" runtime type yet - acc[field.name] = field.runtimeField; - return acc; - }, {} as Record), + runtime_mappings: runtimeMappings, script_fields: scriptedFields.reduce((acc, field) => { acc[field.name] = { script: { From 8397753b9b1cee4f51fe160b6a912ac1cb39f066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Wed, 1 Sep 2021 15:58:08 +0100 Subject: [PATCH 47/60] Update type comment --- src/plugins/data/common/index_patterns/types.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 44d04fb8f9e72..5a4a48f72682b 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -47,9 +47,10 @@ export interface RuntimeField extends ESRuntimeField { } /** - * Runtime fields are like other fields when it comes to formatting or giving - * them a custom label. When adding a new runtime field in the Data view we allow the - * consumer to pass along a "format", "customLabel" or "popularity". + * This is the RuntimeField interface enhanced with Data view field + * configuration: field format definition, customLabel or popularity. + * + * @see {@link RuntimeField} */ export interface EnhancedRuntimeField extends Omit { format?: SerializedFieldFormat; From e2793c07fbf42fd2ffcbd01c519c479ef3259b54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Thu, 2 Sep 2021 11:32:10 +0100 Subject: [PATCH 48/60] Revert "Use the new "DataView" and "DataViewField" interfaces" --- .../index_pattern_field_editor/README.md | 8 ++++---- .../components/delete_field_provider.tsx | 4 ++-- .../components/field_editor_context.tsx | 4 ++-- .../field_editor_flyout_content_container.tsx | 20 +++++++++---------- .../field_format_editor.tsx | 11 +++++++--- .../public/lib/remove_fields.ts | 4 ++-- .../public/lib/serialization.ts | 6 +++--- .../public/open_delete_modal.tsx | 4 ++-- .../public/open_editor.tsx | 10 +++++----- .../public/plugin.test.tsx | 4 ++-- .../public/shared_imports.ts | 3 +-- 11 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/plugins/index_pattern_field_editor/README.md b/src/plugins/index_pattern_field_editor/README.md index 1427643858af5..f6ef06cd77b36 100644 --- a/src/plugins/index_pattern_field_editor/README.md +++ b/src/plugins/index_pattern_field_editor/README.md @@ -27,9 +27,9 @@ Use this method to open the index pattern field editor to either create (runtime This is the only required option. You need to provide the context in which the editor is being consumed. This object has the following properties: -- `indexPattern: DataView`: the index pattern you want to create/edit the field into. +- `indexPattern: IndexPattern`: the index pattern you want to create/edit the field into. -`onSave(field: DataViewField): void` (optional) +`onSave(field: IndexPatternField): void` (optional) You can provide an optional `onSave` handler to be notified when the field has being created/updated. This handler is called after the field has been persisted to the saved object. @@ -47,7 +47,7 @@ Use this method to open a confirmation modal to delete runtime fields from an in You need to provide the context in which the deletion modal is being consumed. This object has the following properties: -- `indexPattern: DataView`: the index pattern you want to delete fields from. +- `indexPattern: IndexPattern`: the index pattern you want to delete fields from. `onDelete(fieldNames: string[]): void` (optional) @@ -63,7 +63,7 @@ This children func React component provides a handler to delete one or multiple #### Props -* `indexPattern: DataView`: the current index pattern. (**required**) +* `indexPattern: IndexPattern`: the current index pattern. (**required**) ```js diff --git a/src/plugins/index_pattern_field_editor/public/components/delete_field_provider.tsx b/src/plugins/index_pattern_field_editor/public/components/delete_field_provider.tsx index f809d0d4bad0e..4ec766fe1c851 100644 --- a/src/plugins/index_pattern_field_editor/public/components/delete_field_provider.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/delete_field_provider.tsx @@ -8,14 +8,14 @@ import React, { useCallback, useRef, useEffect } from 'react'; -import { DataView } from '../shared_imports'; +import { IndexPattern } from '../shared_imports'; import { OpenFieldDeleteModalOptions } from '../open_delete_modal'; import { CloseEditor } from '../types'; type DeleteFieldFunc = (fieldName: string | string[]) => void; export interface Props { children: (deleteFieldHandler: DeleteFieldFunc) => React.ReactNode; - indexPattern: DataView; + indexPattern: IndexPattern; onDelete?: (fieldNames: string[]) => void; } diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_context.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_context.tsx index f2ef205a32d14..88f9352465133 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_context.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_context.tsx @@ -8,12 +8,12 @@ import React, { createContext, useContext, FunctionComponent, useMemo } from 'react'; import { NotificationsStart, CoreStart } from 'src/core/public'; -import type { DataView, DataPublicPluginStart } from '../shared_imports'; +import type { IndexPattern, DataPublicPluginStart } from '../shared_imports'; import { ApiService } from '../lib/api'; import type { InternalFieldType, PluginStart } from '../types'; export interface Context { - indexPattern: DataView; + indexPattern: IndexPattern; fieldTypeToProcess: InternalFieldType; uiSettings: CoreStart['uiSettings']; links: { diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index 92f2bda29a5ee..42b4f20da5397 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -12,8 +12,8 @@ import { i18n } from '@kbn/i18n'; import { METRIC_TYPE } from '@kbn/analytics'; import { - DataViewField, - DataView, + IndexPatternField, + IndexPattern, DataPublicPluginStart, UsageCollectionStart, RuntimeCompositeWithSubFields, @@ -30,18 +30,18 @@ import { FieldPreviewProvider } from './preview'; export interface Props { /** Handler for the "save" footer button */ - onSave: (field: DataViewField[]) => void; + onSave: (field: IndexPatternField[]) => void; /** Handler for the "cancel" footer button */ onCancel: () => void; onMounted?: FieldEditorFlyoutContentProps['onMounted']; /** The docLinks start service from core */ docLinks: DocLinksStart; - /** The data view where the field will be added */ - indexPattern: DataView; + /** The index pattern where the field will be added */ + indexPattern: IndexPattern; /** The Kibana field type of the field to create or edit (default: "runtime") */ fieldTypeToProcess: InternalFieldType; /** Optional field to edit */ - field?: DataViewField; + field?: IndexPatternField; /** Services */ indexPatternService: DataPublicPluginStart['indexPatterns']; notifications: NotificationsStart; @@ -125,7 +125,7 @@ export const FieldEditorFlyoutContentContainer = ({ ); const saveCompositeRuntime = useCallback( - (updatedField: RuntimeCompositeWithSubFields): DataViewField[] => { + (updatedField: RuntimeCompositeWithSubFields): IndexPatternField[] => { if (field?.type !== undefined && field?.type !== 'composite') { // A previous runtime field is now a runtime composite indexPattern.removeRuntimeField(field.name); @@ -146,7 +146,7 @@ export const FieldEditorFlyoutContentContainer = ({ ); const saveRuntimeField = useCallback( - (updatedField: Field): [DataViewField] => { + (updatedField: Field): [IndexPatternField] => { if (field?.type !== undefined && field?.type === 'composite') { // A previous runtime composite is now a runtime field indexPattern.removeRuntimeComposite(field.name); @@ -162,7 +162,7 @@ export const FieldEditorFlyoutContentContainer = ({ ); const updateRuntimeField = useCallback( - (updatedField: Field | RuntimeCompositeWithSubFields): DataViewField[] => { + (updatedField: Field | RuntimeCompositeWithSubFields): IndexPatternField[] => { return (updatedField as RuntimeCompositeWithSubFields).subFields !== undefined ? saveCompositeRuntime(updatedField as RuntimeCompositeWithSubFields) : saveRuntimeField(updatedField as Field); @@ -171,7 +171,7 @@ export const FieldEditorFlyoutContentContainer = ({ ); const updateConcreteField = useCallback( - (updatedField: Field): DataViewField[] => { + (updatedField: Field): IndexPatternField[] => { const editedField = indexPattern.getFieldByName(updatedField.name); if (!editedField) { diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx index 77c42bf21eec3..bbd9fe88f818f 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx @@ -10,18 +10,23 @@ import React, { PureComponent } from 'react'; import { EuiCode, EuiFormRow, EuiSelect } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { KBN_FIELD_TYPES, ES_FIELD_TYPES, DataPublicPluginStart } from 'src/plugins/data/public'; +import { + IndexPattern, + KBN_FIELD_TYPES, + ES_FIELD_TYPES, + DataPublicPluginStart, +} from 'src/plugins/data/public'; import type { FieldFormatInstanceType } from 'src/plugins/field_formats/common'; import { CoreStart } from 'src/core/public'; import { castEsToKbnFieldTypeName } from '../../../../data/public'; import { FormatEditorServiceStart } from '../../service'; -import { SerializedFieldFormat, DataView } from '../../shared_imports'; +import { SerializedFieldFormat } from '../../shared_imports'; import { FormatEditor } from './format_editor'; export interface FormatSelectEditorProps { esTypes: ES_FIELD_TYPES[]; - indexPattern: DataView; + indexPattern: IndexPattern; fieldFormatEditors: FormatEditorServiceStart['fieldFormatEditors']; fieldFormats: DataPublicPluginStart['fieldFormats']; uiSettings: CoreStart['uiSettings']; diff --git a/src/plugins/index_pattern_field_editor/public/lib/remove_fields.ts b/src/plugins/index_pattern_field_editor/public/lib/remove_fields.ts index 43ea99a761ff5..bc9457424099f 100644 --- a/src/plugins/index_pattern_field_editor/public/lib/remove_fields.ts +++ b/src/plugins/index_pattern_field_editor/public/lib/remove_fields.ts @@ -9,13 +9,13 @@ import { i18n } from '@kbn/i18n'; import { METRIC_TYPE } from '@kbn/analytics'; import { NotificationsStart } from 'src/core/public'; -import { DataView, UsageCollectionStart } from '../shared_imports'; +import { IndexPattern, UsageCollectionStart } from '../shared_imports'; import { pluginName } from '../constants'; import { DataPublicPluginStart } from '../../../data/public'; export async function removeFields( fieldNames: string[], - indexPattern: DataView, + indexPattern: IndexPattern, services: { indexPatternService: DataPublicPluginStart['indexPatterns']; usageCollection: UsageCollectionStart; diff --git a/src/plugins/index_pattern_field_editor/public/lib/serialization.ts b/src/plugins/index_pattern_field_editor/public/lib/serialization.ts index 2c024cef8f9f2..8400138d12d39 100644 --- a/src/plugins/index_pattern_field_editor/public/lib/serialization.ts +++ b/src/plugins/index_pattern_field_editor/public/lib/serialization.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import { DataViewField, DataView, RuntimeType } from '../shared_imports'; +import { IndexPatternField, IndexPattern, RuntimeType } from '../shared_imports'; import type { Field } from '../types'; export const deserializeField = ( - indexPattern: DataView, - field?: DataViewField + indexPattern: IndexPattern, + field?: IndexPatternField ): Field | undefined => { if (field === undefined) { return undefined; diff --git a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx index 2b54c11692b32..2620f65d2d568 100644 --- a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx @@ -12,7 +12,7 @@ import { CoreStart, OverlayRef } from 'src/core/public'; import { toMountPoint, DataPublicPluginStart, - DataView, + IndexPattern, UsageCollectionStart, } from './shared_imports'; @@ -23,7 +23,7 @@ import { removeFields } from './lib/remove_fields'; export interface OpenFieldDeleteModalOptions { ctx: { - indexPattern: DataView; + indexPattern: IndexPattern; }; onDelete?: (fieldNames: string[]) => void; fieldName: string | string[]; diff --git a/src/plugins/index_pattern_field_editor/public/open_editor.tsx b/src/plugins/index_pattern_field_editor/public/open_editor.tsx index d436acc4a7bc9..a67c4361178c2 100644 --- a/src/plugins/index_pattern_field_editor/public/open_editor.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_editor.tsx @@ -13,9 +13,9 @@ import { i18n } from '@kbn/i18n'; import { createKibanaReactContext, toMountPoint, - DataViewField, + IndexPatternField, DataPublicPluginStart, - DataView, + IndexPattern, UsageCollectionStart, } from './shared_imports'; @@ -26,9 +26,9 @@ import { FieldEditorLoader } from './components/field_editor_loader'; export interface OpenFieldEditorOptions { ctx: { - indexPattern: DataView; + indexPattern: IndexPattern; }; - onSave?: (field: DataViewField[]) => void; + onSave?: (field: IndexPatternField[]) => void; fieldName?: string; } @@ -80,7 +80,7 @@ export const getFieldEditorOpener = ({ } }; - const onSaveField = (updatedField: DataViewField[]) => { + const onSaveField = (updatedField: IndexPatternField[]) => { closeEditor(); if (onSave) { diff --git a/src/plugins/index_pattern_field_editor/public/plugin.test.tsx b/src/plugins/index_pattern_field_editor/public/plugin.test.tsx index eb8a65131a631..75bb1322d305e 100644 --- a/src/plugins/index_pattern_field_editor/public/plugin.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/plugin.test.tsx @@ -25,7 +25,7 @@ import { usageCollectionPluginMock } from '../../usage_collection/public/mocks'; import { FieldEditorLoader } from './components/field_editor_loader'; import { IndexPatternFieldEditorPlugin } from './plugin'; import { DeleteFieldModal } from './components/confirm_modals/delete_field_modal'; -import { DataView } from './shared_imports'; +import { IndexPattern } from './shared_imports'; const noop = () => {}; @@ -112,7 +112,7 @@ describe('IndexPatternFieldEditorPlugin', () => { }; const { openDeleteModal } = await plugin.start(coreStartMocked, pluginStartMocked); - const indexPatternMock = ({ removeRuntimeField: removeFieldSpy } as unknown) as DataView; + const indexPatternMock = ({ removeRuntimeField: removeFieldSpy } as unknown) as IndexPattern; openDeleteModal({ onDelete: onDeleteSpy, diff --git a/src/plugins/index_pattern_field_editor/public/shared_imports.ts b/src/plugins/index_pattern_field_editor/public/shared_imports.ts index e25cf38e57c0d..bb332a7c0ded3 100644 --- a/src/plugins/index_pattern_field_editor/public/shared_imports.ts +++ b/src/plugins/index_pattern_field_editor/public/shared_imports.ts @@ -6,8 +6,7 @@ * Side Public License, v 1. */ -export { DataPublicPluginStart } from '../../data/public'; -export { DataView, DataViewField } from '../../data/common'; +export { IndexPattern, IndexPatternField, DataPublicPluginStart } from '../../data/public'; export { UsageCollectionStart } from '../../usage_collection/public'; From 759f102ef36d3dbdaa753631ec529ceec0c013c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Thu, 2 Sep 2021 11:48:03 +0100 Subject: [PATCH 49/60] Don't rely on estypes for the RuntimeField interface --- src/plugins/data/common/index_patterns/types.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 5a4a48f72682b..1d0559d394ad0 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -11,24 +11,23 @@ import { ToastInputFields, ErrorToastOptions } from 'src/core/public/notificatio // eslint-disable-next-line import type { SavedObject } from 'src/core/server'; import { IFieldType } from './fields'; +import { RUNTIME_FIELD_TYPES } from './constants'; import { SerializedFieldFormat } from '../../../expressions/common'; import { KBN_FIELD_TYPES, DataViewField } from '..'; import { FieldFormat } from '../../../field_formats/common'; export type FieldFormatMap = Record; -export type RuntimeType = estypes.MappingRuntimeFieldType | 'composite'; +export type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; /** * The RuntimeField that will be sent in the ES Query "runtime_mappings" object - * We extends the object until @elastic/elasticsearch supports "composite" type - * and its "fields" object. - * - * To simplify the consuming code we enforce the script to be `InlineScript` type (and not also `string`) */ -export interface ESRuntimeField extends Omit { +export interface ESRuntimeField { type: RuntimeType; - script?: estypes.InlineScript; + script?: { + source: string; + }; fields?: Record< string, { From 72e010852e19886b26105502347f354b6a6ab56a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Thu, 2 Sep 2021 11:53:11 +0100 Subject: [PATCH 50/60] Don't rely on estypes for the RuntimeField interface (2) --- src/plugins/data/common/index_patterns/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 1d0559d394ad0..4404be20cb0af 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -51,7 +51,7 @@ export interface RuntimeField extends ESRuntimeField { * * @see {@link RuntimeField} */ -export interface EnhancedRuntimeField extends Omit { +export interface EnhancedRuntimeField extends RuntimeField { format?: SerializedFieldFormat; customLabel?: string; popularity?: number; From 71906215991662c8e663ef55b700cfe331cd29ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Thu, 2 Sep 2021 12:49:57 +0100 Subject: [PATCH 51/60] Revert changes server side now that RuntimeField interface is controlled --- .../routes/runtime_fields/update_runtime_field.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/plugins/data/server/index_patterns/routes/runtime_fields/update_runtime_field.ts b/src/plugins/data/server/index_patterns/routes/runtime_fields/update_runtime_field.ts index 13f88188dcd5d..601d4653a54bf 100644 --- a/src/plugins/data/server/index_patterns/routes/runtime_fields/update_runtime_field.ts +++ b/src/plugins/data/server/index_patterns/routes/runtime_fields/update_runtime_field.ts @@ -62,17 +62,11 @@ export const registerUpdateRuntimeFieldRoute = ( throw new ErrorIndexPatternFieldNotFound(id, name); } - // We remove a possible format as the ES "format" (string) is different - // from our Kibana field format definition. - const { format, ...previousField } = existingRuntimeField; - - const updatedRuntimeField: EnhancedRuntimeField = { - ...previousField, - ...runtimeField, - }; - indexPattern.removeRuntimeField(name); - indexPattern.addRuntimeField(name, updatedRuntimeField); + indexPattern.addRuntimeField(name, { + ...existingRuntimeField, + ...runtimeField, + }); await indexPatternsService.updateSavedObject(indexPattern); From de4275dcd574095cb8b63e53028970d09c9d1669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Thu, 2 Sep 2021 13:28:37 +0100 Subject: [PATCH 52/60] Revert "Use the new "DataView" and "DataViewField" interfaces" (2) --- .../public/components/field_editor_flyout_content_container.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index 42b4f20da5397..25c6a6247a72e 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -209,7 +209,7 @@ export const FieldEditorFlyoutContentContainer = ({ setIsSaving(true); try { - const editedFields: DataViewField[] = + const editedFields: IndexPatternField[] = fieldTypeToProcess === 'runtime' ? updateRuntimeField(updatedField) : updateConcreteField(updatedField as Field); From f1f42b3c8661f252a97dbdbe776c331dcce3ccc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 6 Sep 2021 13:30:41 +0100 Subject: [PATCH 53/60] Refactor to not use runtimeCompositeMap --- .../fields/index_pattern_field.ts | 39 +- .../index_patterns/index_pattern.ts | 335 ++++++++---------- .../index_patterns/index_pattern_utils.ts | 97 ----- .../index_patterns/index_patterns.ts | 50 +-- .../data/common/index_patterns/types.ts | 63 ++-- .../components/field_editor/field_editor.tsx | 8 +- .../field_editor_flyout_content.tsx | 57 +-- .../field_editor_flyout_content_container.tsx | 68 ++-- .../preview/field_preview_context.tsx | 4 +- .../public/lib/index.ts | 2 - .../public/lib/runtime_field_validation.ts | 5 +- .../public/lib/serialization.ts | 28 -- .../public/open_editor.tsx | 41 ++- .../public/shared_imports.ts | 4 +- .../public/types.ts | 8 +- 15 files changed, 338 insertions(+), 471 deletions(-) delete mode 100644 src/plugins/data/common/index_patterns/index_patterns/index_pattern_utils.ts delete mode 100644 src/plugins/index_pattern_field_editor/public/lib/serialization.ts diff --git a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts index fae0e14b95c05..1f0e8cf076ebc 100644 --- a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts +++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts @@ -9,7 +9,7 @@ /* eslint-disable max-classes-per-file */ import { KbnFieldType, getKbnFieldType, castEsToKbnFieldTypeName } from '@kbn/field-types'; -import type { RuntimeField } from '../types'; +import type { RuntimeFieldSpec, RuntimeType } from '../types'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import type { IFieldType } from './types'; import { FieldSpec, DataView } from '../..'; @@ -43,7 +43,7 @@ export class DataViewField implements IFieldType { return this.spec.runtimeField; } - public set runtimeField(runtimeField: RuntimeField | undefined) { + public set runtimeField(runtimeField: RuntimeFieldSpec | undefined) { this.spec.runtimeField = runtimeField; } @@ -102,13 +102,29 @@ export class DataViewField implements IFieldType { } public get type() { - return this.runtimeField?.type - ? castEsToKbnFieldTypeName(this.runtimeField?.type) - : this.spec.type; + if (this.isRuntimeField) { + let type: RuntimeType = this.runtimeField?.type!; + if (this.isRuntimeCompositeSubField) { + const [, subFieldName] = this.name.split('.'); + // We return the subField type (with fallback mechanism to "composite" type) + type = this.runtimeField?.fields![subFieldName]?.type ?? this.runtimeField?.type!; + } + return castEsToKbnFieldTypeName(type); + } + + return this.spec.type; } public get esTypes() { - return this.runtimeField?.type ? [this.runtimeField?.type] : this.spec.esTypes; + if (this.isRuntimeField) { + if (this.isRuntimeCompositeSubField) { + const [, subFieldName] = this.name.split('.'); + // We return the subField type (with fallback mechanism to "composite" type) + return [this.runtimeField?.fields![subFieldName]?.type ?? this.runtimeField?.type!]; + } + return [this.runtimeField?.type!]; + } + return this.spec.esTypes; } public get scripted() { @@ -138,6 +154,10 @@ export class DataViewField implements IFieldType { return this.spec.isMapped; } + public get isRuntimeField() { + return !this.isMapped && this.runtimeField !== undefined; + } + // not writable, not serialized public get sortable() { return ( @@ -206,6 +226,13 @@ export class DataViewField implements IFieldType { isMapped: this.isMapped, }; } + + private get isRuntimeCompositeSubField(): boolean { + if (!this.isRuntimeField) { + return false; + } + return this.runtimeField!.type === 'composite'; + } } /** diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 818b410967f9a..1cdb84d238cf2 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -11,14 +11,7 @@ import _, { each, reject } from 'lodash'; import { castEsToKbnFieldTypeName } from '@kbn/field-types'; import { FieldAttrs, FieldAttrSet, DataViewAttributes } from '../..'; -import type { - EnhancedRuntimeField, - RuntimeField, - RuntimeType, - RuntimeComposite, - RuntimeCompositeWithSubFields, - ESRuntimeField, -} from '../types'; +import type { RuntimeField, RuntimeFieldSpec, RuntimeType, FieldConfiguration } from '../types'; import { DuplicateField } from '../../../../kibana_utils/common'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES, IIndexPattern, IFieldType } from '../../../common'; @@ -28,7 +21,6 @@ import { flattenHitWrapper } from './flatten_hit'; import { FieldFormatsStartCommon, FieldFormat } from '../../../../field_formats/common'; import { DataViewSpec, TypeMeta, SourceFilter, DataViewFieldMap } from '../types'; import { SerializedFieldFormat } from '../../../../expressions/common'; -import { getRuntimeFieldsFromMap, getRuntimeCompositeFieldsFromMap } from './index_pattern_utils'; interface DataViewDeps { spec?: DataViewSpec; @@ -87,8 +79,7 @@ export class DataView implements IIndexPattern { private shortDotsEnable: boolean = false; private fieldFormats: FieldFormatsStartCommon; private fieldAttrs: FieldAttrs; - private runtimeFieldMap: Record; - private runtimeCompositeMap: Record; + private runtimeFieldMap: Record; /** * prevents errors when index pattern exists before indices @@ -127,7 +118,6 @@ export class DataView implements IIndexPattern { this.intervalName = spec.intervalName; this.allowNoIndex = spec.allowNoIndex || false; this.runtimeFieldMap = spec.runtimeFieldMap || {}; - this.runtimeCompositeMap = spec.runtimeCompositeMap || {}; } /** @@ -228,7 +218,6 @@ export class DataView implements IIndexPattern { type: this.type, fieldFormats: this.fieldFormatMap, runtimeFieldMap: this.runtimeFieldMap, - runtimeCompositeMap: this.runtimeCompositeMap, fieldAttrs: this.fieldAttrs, intervalName: this.intervalName, allowNoIndex: this.allowNoIndex, @@ -339,7 +328,6 @@ export class DataView implements IIndexPattern { : JSON.stringify(this.fieldFormatMap); const fieldAttrs = this.getFieldAttrs(); const runtimeFieldMap = this.runtimeFieldMap; - const runtimeCompositeMap = this.runtimeCompositeMap; return { fieldAttrs: fieldAttrs ? JSON.stringify(fieldAttrs) : undefined, @@ -353,7 +341,6 @@ export class DataView implements IIndexPattern { typeMeta: JSON.stringify(this.typeMeta ?? {}), allowNoIndex: this.allowNoIndex ? this.allowNoIndex : undefined, runtimeFieldMap: runtimeFieldMap ? JSON.stringify(runtimeFieldMap) : undefined, - runtimeCompositeMap: runtimeCompositeMap ? JSON.stringify(runtimeCompositeMap) : undefined, }; } @@ -375,51 +362,103 @@ export class DataView implements IIndexPattern { /** * Add a runtime field - Appended to existing mapped field or a new field is - * created as appropriate. We can pass along with the fields a "format" definition or "customLabel" to avoid calling - * separately `setFieldCustomLabel()` and `setFieldFormat()`. + * created as appropriate. * @param name Field name - * @param enhancedRuntimeField Runtime field definition + * @param runtimeField Runtime field definition */ - addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): DataViewField { - const existRuntimeCompositeWithSameName = this.getRuntimeComposite(name) !== null; + addRuntimeField(name: string, runtimeField: RuntimeField): DataViewField[] { + const { type, script, fields, customLabel, format, popularity } = runtimeField; - if (existRuntimeCompositeWithSameName) { - throw new Error( - `Can't add runtime field ["${name}"] as there is already a runtime composite with the same name.` + if (type === 'composite' && (fields === undefined || Object.keys(fields).length === 0)) { + throw new Error(`Can't add composite runtime field [name = ${name}] without subfields.`); + } + + let runtimeFieldSpecFields: RuntimeFieldSpec['fields'] | undefined; + + if (fields !== undefined) { + runtimeFieldSpecFields = Object.entries(fields).reduce( + (acc, [subFieldName, subField]) => { + return { + ...acc, + [subFieldName]: { + type: subField.type, + }, + }; + }, + {} ); } - const { type, script, parentComposite, customLabel, format, popularity } = enhancedRuntimeField; + const runtimeFieldSpec: RuntimeFieldSpec = { + type, + script, + fields: runtimeFieldSpecFields, + }; - const runtimeField: RuntimeField = { type, script, parentComposite }; - this.runtimeFieldMap[name] = runtimeField; + const updateOrCreateField = ( + fieldName: string, + fieldType: RuntimeType, + config: FieldConfiguration + ): DataViewField => { + // Create the field if it does not exist or update an existing one + let createdField: DataViewField | undefined; + const existingField = this.getFieldByName(fieldName); + + if (existingField) { + existingField.runtimeField = runtimeFieldSpec; + } else { + createdField = this.fields.add({ + name: fieldName, + runtimeField: runtimeFieldSpec, + type: castEsToKbnFieldTypeName(fieldType), + aggregatable: true, + searchable: true, + count: config.popularity ?? 0, + readFromDocValues: false, + }); + } - // Create the field if it does not exist or update an existing one - let createdField: DataViewField | undefined; - const existingField = this.getFieldByName(name); - if (existingField) { - existingField.runtimeField = runtimeField; - } else { - createdField = this.fields.add({ - name, - runtimeField, - type: castEsToKbnFieldTypeName(type), - aggregatable: true, - searchable: true, - count: popularity ?? 0, - readFromDocValues: false, - }); - } + // Apply configuration to the field + this.setFieldCustomLabel(fieldName, config.customLabel); + if (config.format) { + this.setFieldFormat(fieldName, config.format); + } else if (config.format === null) { + this.deleteFieldFormat(fieldName); + } - // Apply configuration to the field - this.setFieldCustomLabel(name, customLabel); - if (format) { - this.setFieldFormat(name, format); + return existingField ?? createdField!; + }; + + let dataViewFields: DataViewField[]; + + if (type === 'composite') { + // Make sure no field with the same name already exist + if (this.getFieldByName(name) !== undefined) { + throw new Error( + `Can't create composite runtime field ["${name}"] as there is already a field with this name` + ); + } + + // We first remove the runtime composite field with the same name which will remove all of its subFields. + // This guarantees that we don't leave behind orphan data view fields + this.removeRuntimeField(name); + + // For composite runtime field we don't add those to the field list as + // composite runtime fields are **not** fields but **holder** of fields. + // What we do add to the field list are all **their subFields**. + dataViewFields = Object.entries(fields!).map(([subFieldName, subField]) => + updateOrCreateField(`${name}.${subFieldName}`, subField.type, { + customLabel: subField.customLabel, + format: subField.format, + popularity: subField.popularity, + }) + ); } else { - this.deleteFieldFormat(name); + dataViewFields = [updateOrCreateField(name, type, { customLabel, format, popularity })]; } - return existingField ?? createdField!; + this.runtimeFieldMap[name] = runtimeFieldSpec; + return dataViewFields; } /** @@ -435,14 +474,67 @@ export class DataView implements IIndexPattern { * @param name */ getRuntimeField(name: string): RuntimeField | null { - return this.runtimeFieldMap[name] ?? null; + if (!this.runtimeFieldMap[name]) { + return null; + } + + const { type, script, fields } = { ...this.runtimeFieldMap[name] }; + const runtimeField: RuntimeField = { + type, + script, + }; + + if (type === 'composite') { + const subFields = Object.entries(fields!).reduce( + (acc, [subFieldName, subField]) => { + const fieldFullName = `${name}.${subFieldName}`; + const dataViewField = this.getFieldByName(fieldFullName); + if (!dataViewField) { + // We should never enter here as all composite runtime subfield + // are converted to data view fields. + return acc; + } + return { + ...acc, + [subFieldName]: { + type: subField.type, + format: this.getFormatterForFieldNoDefault(fieldFullName)?.toJSON(), + customLabel: dataViewField.customLabel, + popularity: dataViewField.count, + }, + }; + }, + {} + ); + + runtimeField.fields = subFields; + } else { + const dataViewField = this.getFieldByName(name); + if (dataViewField) { + runtimeField.customLabel = dataViewField.customLabel; + runtimeField.popularity = dataViewField.count; + runtimeField.format = this.getFormatterForFieldNoDefault(name)?.toJSON(); + } + } + + return runtimeField; + } + + getAllRuntimeFields(): Record { + return Object.keys(this.runtimeFieldMap).reduce>( + (acc, fieldName) => ({ + ...acc, + [fieldName]: this.getRuntimeField(fieldName)!, + }), + {} + ); } /** * Replaces all existing runtime fields with new fields * @param newFields */ - replaceAllRuntimeFields(newFields: Record) { + replaceAllRuntimeFields(newFields: Record) { const oldRuntimeFieldNames = Object.keys(this.runtimeFieldMap); oldRuntimeFieldNames.forEach((name) => { this.removeRuntimeField(name); @@ -462,147 +554,34 @@ export class DataView implements IIndexPattern { const existingField = this.getFieldByName(name); if (existingField) { - const parentCompositeName = existingField.runtimeField?.parentComposite; - const hasParentComposite = - parentCompositeName !== undefined && - this.getRuntimeComposite(parentCompositeName!) !== null; - if (hasParentComposite) { - throw new Error( - `Can't remove runtime field ["${name}"] as it belongs to the composite runtime ["${parentCompositeName}"]` - ); - } - if (existingField.isMapped) { // mapped field, remove runtimeField def existingField.runtimeField = undefined; } else { this.fields.remove(existingField); } - } - delete this.runtimeFieldMap[name]; - } - - /** - * Create a runtime composite and add its subFields to the index pattern fields list - * @param name - The runtime composite name - * @param runtimeComposite - The runtime composite definition - */ - addRuntimeComposite( - name: string, - runtimeComposite: RuntimeCompositeWithSubFields - ): DataViewField[] { - if (!runtimeComposite.subFields || Object.keys(runtimeComposite.subFields).length === 0) { - throw new Error(`Can't save runtime composite [name = ${name}] without subfields.`); - } - - // We first remove the runtime composite with the same name which will remove all of its subFields. - // This guarantees that we don't leave behind orphan runtime fields (with a "compositeParent"). - this.removeRuntimeComposite(name); - - const { script, subFields } = runtimeComposite; - - const fieldsCreated: DataViewField[] = []; - - for (const [subFieldName, subField] of Object.entries(subFields)) { - const field = this.addRuntimeField(`${name}.${subFieldName}`, { - ...subField, - parentComposite: name, - }); - fieldsCreated.push(field); - } - - this.runtimeCompositeMap[name] = { - name, - script, - // We only need to keep a reference of the subFields names - subFields: Object.keys(subFields), - }; - - return fieldsCreated; - } - - /** - * Returns runtime composite if exists - * @param name - */ - getRuntimeComposite(name: string): RuntimeComposite | null { - return this.runtimeCompositeMap[name] ?? null; - } - - /** - * Returns runtime composite (if exists) with its subFields - * @param name - */ - getRuntimeCompositeWithSubFields(name: string): RuntimeCompositeWithSubFields | null { - const runtimeComposite = this.getRuntimeComposite(name); - - if (!runtimeComposite) { - return null; - } - - const subFields = runtimeComposite.subFields.reduce((acc, subFieldName) => { - const field = this.getFieldByName(subFieldName); - - if (!field) { - // This condition should never happen - return acc; - } - - const runtimeField: EnhancedRuntimeField = { - type: field.type as RuntimeType, - customLabel: field.customLabel, - popularity: field.count, - parentComposite: name, - format: this.getFormatterForFieldNoDefault(field.name)?.toJSON(), - }; - - return { - ...acc, - [subFieldName]: runtimeField, - }; - }, {} as Record); - - return { - ...runtimeComposite, - subFields, - }; - } - - /** - * Return all the runtime composite fields - */ - getAllRuntimeComposites(): Record { - return _.cloneDeep(this.runtimeCompositeMap); - } - - /** - * Remove a runtime composite with its associated subFields - * @param name - Runtime composite name to remove - */ - removeRuntimeComposite(name: string) { - const existingRuntimeComposite = this.getRuntimeComposite(name); - - if (!!existingRuntimeComposite) { - delete this.runtimeCompositeMap[name]; - - // Remove all subFields - for (const subFieldName of existingRuntimeComposite.subFields) { - this.removeRuntimeField(`${name}.${subFieldName}`); + } else { + const runtimeFieldSpec = this.runtimeFieldMap[name]; + + if (runtimeFieldSpec?.type === 'composite') { + // If we remove a "composite" runtime field we loop through each of its + // subFields and remove them from the field list + Object.keys(runtimeFieldSpec.fields!).forEach((subFieldName) => { + const subField = this.getFieldByName(`${name}.${subFieldName}`); + if (subField) { + this.fields.remove(subField); + } + }); } } + delete this.runtimeFieldMap[name]; } /** * Return the "runtime_mappings" section of the ES search query */ - getRuntimeMappings(): Record { - return { - ...getRuntimeFieldsFromMap(this.runtimeFieldMap), - ...getRuntimeCompositeFieldsFromMap( - this.runtimeCompositeMap, - this.getRuntimeField.bind(this) - ), - }; + getRuntimeMappings(): Record { + return _.cloneDeep(this.runtimeFieldMap); } /** diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern_utils.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern_utils.ts deleted file mode 100644 index 17b50ac64a89e..0000000000000 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern_utils.ts +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import type { RuntimeField, RuntimeType, RuntimeComposite, ESRuntimeField } from '../types'; - -/** - * Method to aggregate all the runtime fields which are **not** created - * from a parent composite runtime field. - * @returns A map of runtime fields - */ -export const getRuntimeFieldsFromMap = ( - runtimeFieldMap: Record -): Record => { - return Object.entries(runtimeFieldMap).reduce((acc, [name, field]) => { - const { type, script, parentComposite } = field; - - if (parentComposite !== undefined) { - return acc; - } - - const runtimeFieldRequest: RuntimeField = { - type, - script, - }; - - return { - ...acc, - [name]: runtimeFieldRequest, - }; - }, {}); -}; - -/** - * This method reads all the runtime composite fields - * and aggregate the subFields - * - * { - * "compositeName": { - * "type": "composite", - * "script": "emit(...)" // script that emits multiple values - * "fields": { // map of subFields available in the Query - * "field_1": { - * "type": "keyword" - * }, - * "field_2": { - * "type": "ip" - * }, - * } - * } - * } - * - * @returns A map of runtime fields - */ -export const getRuntimeCompositeFieldsFromMap = ( - runtimeCompositeMap: Record, - runtimeFieldGetter: (name: string) => RuntimeField | null -): Record => { - return Object.entries(runtimeCompositeMap).reduce((acc, [name, runtimeComposite]) => { - const { script, subFields } = runtimeComposite; - - // Aggregate all the subFields belonging to this runtimeComposite - const fields: ESRuntimeField['fields'] = subFields.reduce((accFields, subFieldName) => { - const subField = runtimeFieldGetter(`${name}.${subFieldName}`); - - if (!subField) { - return accFields; - } - - return { - ...accFields, - [subFieldName]: { type: subField.type }, - }; - }, {} as Record); - - if (Object.keys(fields).length === 0) { - // This should never happen, but sending a composite runtime field - // with an empty "fields" will break the Query - return acc; - } - - const runtimeFieldRequest: ESRuntimeField = { - type: 'composite', - script, - fields, - }; - - return { - ...acc, - [name]: runtimeFieldRequest, - }; - }, {}); -}; diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index 24c45d4ea0cad..3201c443ebe3e 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -14,7 +14,7 @@ import { castEsToKbnFieldTypeName } from '@kbn/field-types'; import { DATA_VIEW_SAVED_OBJECT_TYPE, SavedObjectsClientCommon } from '../..'; import { createDataViewCache } from '.'; -import type { RuntimeField, RuntimeComposite } from '../types'; +import type { RuntimeField, RuntimeFieldSpec, RuntimeType } from '../types'; import { DataView } from './index_pattern'; import { createEnsureDefaultDataView, EnsureDefaultDataView } from './ensure_default_index_pattern'; import { @@ -382,7 +382,6 @@ export class DataViewsService { sourceFilters, fieldFormatMap, runtimeFieldMap, - runtimeCompositeMap, typeMeta, type, fieldAttrs, @@ -398,9 +397,6 @@ export class DataViewsService { const parsedRuntimeFieldMap: Record = runtimeFieldMap ? JSON.parse(runtimeFieldMap) : {}; - const parsedRuntimeCompositeMap: Record = runtimeCompositeMap - ? JSON.parse(runtimeCompositeMap) - : {}; return { id, @@ -416,7 +412,6 @@ export class DataViewsService { fieldAttrs: parsedFieldAttrs, allowNoIndex, runtimeFieldMap: parsedRuntimeFieldMap, - runtimeCompositeMap: parsedRuntimeCompositeMap, }; }; @@ -461,20 +456,35 @@ export class DataViewsService { spec.fieldAttrs ); + const addRuntimeFieldToSpecFields = ( + name: string, + fieldType: RuntimeType, + runtimeField: RuntimeFieldSpec + ) => { + spec.fields![name] = { + name, + type: castEsToKbnFieldTypeName(fieldType), + runtimeField, + aggregatable: true, + searchable: true, + readFromDocValues: false, + customLabel: spec.fieldAttrs?.[name]?.customLabel, + count: spec.fieldAttrs?.[name]?.count, + }; + }; + // CREATE RUNTIME FIELDS - for (const [key, value] of Object.entries(runtimeFieldMap || {})) { + for (const [name, runtimeField] of Object.entries(runtimeFieldMap || {})) { // do not create runtime field if mapped field exists - if (!spec.fields[key]) { - spec.fields[key] = { - name: key, - type: castEsToKbnFieldTypeName(value.type), - runtimeField: value, - aggregatable: true, - searchable: true, - readFromDocValues: false, - customLabel: spec.fieldAttrs?.[key]?.customLabel, - count: spec.fieldAttrs?.[key]?.count, - }; + if (!spec.fields[name]) { + // For composite runtime field we add the subFields, **not** the composite + if (runtimeField.type === 'composite') { + Object.entries(runtimeField.fields!).forEach(([subFieldName, subField]) => { + addRuntimeFieldToSpecFields(`${name}.${subFieldName}`, subField.type, runtimeField); + }); + } else { + addRuntimeFieldToSpecFields(name, runtimeField.type, runtimeField); + } } } } catch (err) { @@ -498,10 +508,6 @@ export class DataViewsService { ? JSON.parse(savedObject.attributes.fieldFormatMap) : {}; - spec.runtimeCompositeMap = savedObject.attributes.runtimeCompositeMap - ? JSON.parse(savedObject.attributes.runtimeCompositeMap) - : {}; - const indexPattern = await this.create(spec, true); indexPattern.resetOriginalSavedObjectBody(); return indexPattern; diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 4404be20cb0af..ce6853828c8f8 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -20,29 +20,32 @@ export type FieldFormatMap = Record; export type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; -/** - * The RuntimeField that will be sent in the ES Query "runtime_mappings" object - */ -export interface ESRuntimeField { +export type RuntimeTypeExceptComposite = Exclude; + +export interface RuntimeFieldBase { type: RuntimeType; script?: { source: string; }; +} + +/** + * The RuntimeField that will be sent in the ES Query "runtime_mappings" object + */ +export interface RuntimeFieldSpec extends RuntimeFieldBase { fields?: Record< string, { // It is not recursive, we can't create a composite inside a composite. - type: Omit; + type: RuntimeTypeExceptComposite; } >; } -/** - * The RuntimeField which is saved in the Data View saved object. We extend it to - * keep a reference to a possible parent composite object. - */ -export interface RuntimeField extends ESRuntimeField { - parentComposite?: string; +export interface FieldConfiguration { + format?: SerializedFieldFormat | null; + customLabel?: string; + popularity?: number; } /** @@ -51,37 +54,14 @@ export interface RuntimeField extends ESRuntimeField { * * @see {@link RuntimeField} */ -export interface EnhancedRuntimeField extends RuntimeField { - format?: SerializedFieldFormat; - customLabel?: string; - popularity?: number; +export interface RuntimeField extends RuntimeFieldBase, FieldConfiguration { + fields?: Record; } -/** - * When we add a runtime field of "composite" type we are actually adding a _holder_ - * object with runtime fields inside of it. - * The RuntimeComposite interface is this holder of fields. - * It has a name, a script and an array references to the runtime fields it holds. - */ -export interface RuntimeComposite { - name: string; - script: { - source: string; - }; - subFields: string[]; +export interface RuntimeFieldSubField extends FieldConfiguration { + type: RuntimeTypeExceptComposite; } -/** - * This is the same as the RuntimeComposite interface but instead of - * returning an array of references to the subFields we return a **map** of subfields - * with their possible format, custom label and popularity. - * - * @see {@link RuntimeComposite} - */ -export type RuntimeCompositeWithSubFields = Omit & { - subFields: Record; -}; - /** * @deprecated * IIndexPattern allows for an IndexPattern OR an index pattern saved object @@ -117,7 +97,6 @@ export interface DataViewAttributes { fieldFormatMap?: string; fieldAttrs?: string; runtimeFieldMap?: string; - runtimeCompositeMap?: string; /** * prevents errors when index pattern exists before indices */ @@ -275,11 +254,10 @@ export interface FieldSpec extends DataViewFieldBase { readFromDocValues?: boolean; indexed?: boolean; customLabel?: string; - runtimeField?: RuntimeField; + runtimeField?: RuntimeFieldSpec; // not persisted shortDotsEnable?: boolean; isMapped?: boolean; - parent?: string; } export type DataViewFieldMap = Record; @@ -314,8 +292,7 @@ export interface DataViewSpec { typeMeta?: TypeMeta; type?: string; fieldFormats?: Record; - runtimeFieldMap?: Record; - runtimeCompositeMap?: Record; + runtimeFieldMap?: Record; fieldAttrs?: FieldAttrs; allowNoIndex?: boolean; } diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.tsx index b46d587dc4146..f01e55a9fd32b 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.tsx @@ -130,9 +130,12 @@ const formDeserializer = (field: Field): FieldFormInternal => { fieldType = [{ label: label ?? field.type, value: field.type as RuntimeType }]; } + const format = field.format === null ? undefined : field.format; + return { ...field, type: fieldType, + format, __meta__: { isCustomLabelVisible: field.customLabel !== undefined, isValueVisible: field.script !== undefined, @@ -143,9 +146,12 @@ const formDeserializer = (field: Field): FieldFormInternal => { }; const formSerializer = (field: FieldFormInternal): Field => { - const { __meta__, type, ...rest } = field; + const { __meta__, type, format, ...rest } = field; return { type: type[0].value!, + // By passing "null" we are explicitly telling DataView to remove the + // format if there is one defined for the field. + format: format === undefined ? null : format, ...rest, }; }; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx index bb7881508b84f..65521836db7d5 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx @@ -7,6 +7,7 @@ */ import React, { useState, useCallback, useMemo, useEffect } from 'react'; +import { estypes } from '@elastic/elasticsearch'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { @@ -23,11 +24,7 @@ import { import type { Field } from '../types'; import { RuntimeFieldPainlessError } from '../lib'; import { euiFlyoutClassname } from '../constants'; -import type { - RuntimeField, - EnhancedRuntimeField, - RuntimeCompositeWithSubFields, -} from '../shared_imports'; +import type { RuntimeFieldSubField } from '../shared_imports'; import { FlyoutPanels } from './flyout_panels'; import { useFieldEditorContext } from './field_editor_context'; import { FieldEditor, FieldEditorFormState } from './field_editor/field_editor'; @@ -55,13 +52,15 @@ export interface Props { /** * Handler for the "save" footer button */ - onSave: (field: Field | RuntimeCompositeWithSubFields) => void; + onSave: (field: Field) => void; /** * Handler for the "cancel" footer button */ onCancel: () => void; /** Handler to validate the script */ - runtimeFieldValidator: (field: RuntimeField) => Promise; + runtimeFieldValidator: ( + field: estypes.MappingRuntimeField + ) => Promise; /** Optional field to process */ field?: Field; isSavingField: boolean; @@ -126,24 +125,31 @@ const FieldEditorFlyoutContentComponent = ({ }, [isFormModified]); const addSubfieldsToField = useCallback( - (_field: Field): Field | RuntimeCompositeWithSubFields => { - if (_field.type === 'composite' && fieldsInScript.length > 0) { - const subFields = fieldsInScript.reduce((acc, subFieldName) => { - const subField: EnhancedRuntimeField = { - // We hardcode "keyword" and "format" for now until the UI - // lets us define them for each of the sub fields - type: 'keyword' as const, - format: _field.format, - }; - - acc[subFieldName] = subField; - return acc; - }, {} as Record); - - const updatedField: RuntimeCompositeWithSubFields = { - name: _field.name, - script: _field.script!, - subFields, + (_field: Field): Field => { + const { name, type, script, format } = _field; + + // This is a **temporary hack** to create the subfields. + // It will be replaced by a UI where the user will set the type and format + // of each subField. + if (type === 'composite' && fieldsInScript.length > 0) { + const fields = fieldsInScript.reduce>( + (acc, subFieldName) => { + const subField: RuntimeFieldSubField = { + type: 'keyword' as const, + format, + }; + + acc[subFieldName] = subField; + return acc; + }, + {} + ); + + const updatedField: Field = { + name, + type, + script, + fields, }; return updatedField; @@ -164,6 +170,7 @@ const FieldEditorFlyoutContentComponent = ({ setIsValidating(true); const error = await runtimeFieldValidator({ + // @ts-expect-error @elastic/elasticsearch does not support "composite" type yet type: updatedField.type, script: updatedField.script, }); diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index 25c6a6247a72e..a3587ea2b79a8 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -16,11 +16,10 @@ import { IndexPattern, DataPublicPluginStart, UsageCollectionStart, - RuntimeCompositeWithSubFields, } from '../shared_imports'; import type { Field, PluginStart, InternalFieldType } from '../types'; import { pluginName } from '../constants'; -import { deserializeField, getRuntimeFieldValidator, getLinks, ApiService } from '../lib'; +import { getRuntimeFieldValidator, getLinks, ApiService } from '../lib'; import { FieldEditorFlyoutContent, Props as FieldEditorFlyoutContentProps, @@ -41,7 +40,7 @@ export interface Props { /** The Kibana field type of the field to create or edit (default: "runtime") */ fieldTypeToProcess: InternalFieldType; /** Optional field to edit */ - field?: IndexPatternField; + field?: Field; /** Services */ indexPatternService: DataPublicPluginStart['indexPatterns']; notifications: NotificationsStart; @@ -78,14 +77,15 @@ export const FieldEditorFlyoutContentContainer = ({ fieldFormats, uiSettings, }: Props) => { - const fieldToEdit = deserializeField(indexPattern, field); const [isSaving, setIsSaving] = useState(false); const { fields } = indexPattern; const namesNotAllowed = useMemo(() => { const fieldNames = indexPattern.fields.map((fld) => fld.name); - const runtimeCompositeNames = Object.keys(indexPattern.getAllRuntimeComposites()); + const runtimeCompositeNames = Object.entries(indexPattern.getAllRuntimeFields()) + .filter(([, _runtimeField]) => _runtimeField.type === 'composite') + .map(([_runtimeFieldName]) => _runtimeFieldName); return { fields: fieldNames, runtimeComposites: runtimeCompositeNames, @@ -124,50 +124,22 @@ export const FieldEditorFlyoutContentContainer = ({ [apiService, search, notifications] ); - const saveCompositeRuntime = useCallback( - (updatedField: RuntimeCompositeWithSubFields): IndexPatternField[] => { - if (field?.type !== undefined && field?.type !== 'composite') { - // A previous runtime field is now a runtime composite - indexPattern.removeRuntimeField(field.name); - } else if (field?.name && field.name !== updatedField.name) { - // rename an existing runtime composite - indexPattern.removeRuntimeComposite(field.name); - } - - const { name, script, subFields } = updatedField; - - return indexPattern.addRuntimeComposite(name, { - name, - script: script!, - subFields, - }); - }, - [field?.name, field?.type, indexPattern] - ); - - const saveRuntimeField = useCallback( - (updatedField: Field): [IndexPatternField] => { - if (field?.type !== undefined && field?.type === 'composite') { - // A previous runtime composite is now a runtime field - indexPattern.removeRuntimeComposite(field.name); - } else if (field?.name && field.name !== updatedField.name) { - // rename an existing runtime field - indexPattern.removeRuntimeField(field.name); + const updateRuntimeField = useCallback( + (updatedField: Field): IndexPatternField[] => { + const nameHasChanged = Boolean(field) && field!.name !== updatedField.name; + const typeHasChanged = Boolean(field) && field!.type !== updatedField.type; + const hasChangeToOrFromComposite = + typeHasChanged && (field!.type === 'composite' || updatedField.type === 'composite'); + + if (nameHasChanged || hasChangeToOrFromComposite) { + // Rename an existing runtime field or the type has changed from being a "composite" + // to any other type or from any other type to "composite" + indexPattern.removeRuntimeField(field!.name); } - const fieldCreated = indexPattern.addRuntimeField(updatedField.name, updatedField); - return [fieldCreated]; - }, - [field?.name, field?.type, indexPattern] - ); - - const updateRuntimeField = useCallback( - (updatedField: Field | RuntimeCompositeWithSubFields): IndexPatternField[] => { - return (updatedField as RuntimeCompositeWithSubFields).subFields !== undefined - ? saveCompositeRuntime(updatedField as RuntimeCompositeWithSubFields) - : saveRuntimeField(updatedField as Field); + return indexPattern.addRuntimeField(updatedField.name, updatedField); }, - [saveCompositeRuntime, saveRuntimeField] + [field, indexPattern] ); const updateConcreteField = useCallback( @@ -196,7 +168,7 @@ export const FieldEditorFlyoutContentContainer = ({ ); const saveField = useCallback( - async (updatedField: Field | RuntimeCompositeWithSubFields) => { + async (updatedField: Field) => { try { usageCollection.reportUiCounter( pluginName, @@ -261,7 +233,7 @@ export const FieldEditorFlyoutContentContainer = ({ onSave={saveField} onCancel={onCancel} onMounted={onMounted} - field={fieldToEdit} + field={field} runtimeFieldValidator={validateRuntimeField} isSavingField={isSaving} /> diff --git a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx index 03948bae1f684..78d92e9085f44 100644 --- a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx @@ -22,7 +22,7 @@ import { get } from 'lodash'; import type { FieldPreviewContext } from '../../types'; import { parseEsError } from '../../lib/runtime_field_validation'; -import { RuntimeType, RuntimeField, SerializedFieldFormat } from '../../shared_imports'; +import { RuntimeType, SerializedFieldFormat } from '../../shared_imports'; import { useFieldEditorContext } from '../field_editor_context'; type From = 'cluster' | 'custom'; @@ -46,7 +46,7 @@ interface Params { name: string | null; index: string | null; type: RuntimeType | null; - script: Required['script'] | null; + script: { source: string } | null; format: SerializedFieldFormat | null; document: EsDocument | null; } diff --git a/src/plugins/index_pattern_field_editor/public/lib/index.ts b/src/plugins/index_pattern_field_editor/public/lib/index.ts index 336de9574c460..0b94434f67f0a 100644 --- a/src/plugins/index_pattern_field_editor/public/lib/index.ts +++ b/src/plugins/index_pattern_field_editor/public/lib/index.ts @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -export { deserializeField } from './serialization'; - export { getLinks } from './documentation'; export { diff --git a/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.ts b/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.ts index 5553e8d583417..244df1ee10881 100644 --- a/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.ts +++ b/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ import { i18n } from '@kbn/i18n'; +import { estypes } from '@elastic/elasticsearch'; -import { DataPublicPluginStart, RuntimeField } from '../shared_imports'; +import { DataPublicPluginStart } from '../shared_imports'; export interface RuntimeFieldPainlessError { message: string; @@ -94,7 +95,7 @@ export const parseEsError = ( export const getRuntimeFieldValidator = ( index: string, searchService: DataPublicPluginStart['search'] -) => async (runtimeField: RuntimeField) => { +) => async (runtimeField: estypes.MappingRuntimeField) => { return await searchService .search({ params: { diff --git a/src/plugins/index_pattern_field_editor/public/lib/serialization.ts b/src/plugins/index_pattern_field_editor/public/lib/serialization.ts deleted file mode 100644 index 8400138d12d39..0000000000000 --- a/src/plugins/index_pattern_field_editor/public/lib/serialization.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { IndexPatternField, IndexPattern, RuntimeType } from '../shared_imports'; -import type { Field } from '../types'; - -export const deserializeField = ( - indexPattern: IndexPattern, - field?: IndexPatternField -): Field | undefined => { - if (field === undefined) { - return undefined; - } - - return { - name: field.name, - type: (field?.esTypes ? field.esTypes[0] : 'keyword') as RuntimeType, - script: field.runtimeField ? field.runtimeField.script : undefined, - customLabel: field.customLabel, - popularity: field.count, - format: indexPattern.getFormatterForFieldNoDefault(field.name)?.toJSON(), - }; -}; diff --git a/src/plugins/index_pattern_field_editor/public/open_editor.tsx b/src/plugins/index_pattern_field_editor/public/open_editor.tsx index a67c4361178c2..01d89585ce24c 100644 --- a/src/plugins/index_pattern_field_editor/public/open_editor.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_editor.tsx @@ -17,9 +17,10 @@ import { DataPublicPluginStart, IndexPattern, UsageCollectionStart, + RuntimeType, } from './shared_imports'; -import type { PluginStart, InternalFieldType, CloseEditor } from './types'; +import type { PluginStart, InternalFieldType, CloseEditor, Field } from './types'; import type { ApiService } from './lib/api'; import { euiFlyoutClassname } from './constants'; import { FieldEditorLoader } from './components/field_editor_loader'; @@ -88,9 +89,9 @@ export const getFieldEditorOpener = ({ } }; - const field = fieldName ? indexPattern.getFieldByName(fieldName) : undefined; + const dataViewField = fieldName ? indexPattern.getFieldByName(fieldName) : undefined; - if (fieldName && !field) { + if (fieldName && !dataViewField) { const err = i18n.translate('indexPatternFieldEditor.noSuchFieldName', { defaultMessage: "Field named '{fieldName}' not found on index pattern", values: { fieldName }, @@ -100,15 +101,37 @@ export const getFieldEditorOpener = ({ } const isNewRuntimeField = !fieldName; - const isExistingRuntimeField = field && field.runtimeField && !field.isMapped; + const isExistingRuntimeField = + dataViewField && dataViewField.runtimeField && !dataViewField.isMapped; const fieldTypeToProcess: InternalFieldType = isNewRuntimeField || isExistingRuntimeField ? 'runtime' : 'concrete'; - if (field?.runtimeField?.parentComposite !== undefined) { - console.log( // eslint-disable-line - 'TODO: display a modal to indicate that this field needs to be edited through its parent.' - ); - return closeEditor; + let field: Field | undefined; + if (dataViewField) { + if (isExistingRuntimeField && dataViewField.runtimeField!.type === 'composite') { + // We are editing a composite runtime **subField**. + // We need to access the parent composite. + const [compositeName] = fieldName!.split('.'); + field = { + name: compositeName, + ...indexPattern.getRuntimeField(compositeName)!, + }; + } else if (isExistingRuntimeField) { + // Runtime field + field = { + name: fieldName!, + ...indexPattern.getRuntimeField(fieldName!)!, + }; + } else { + // Concrete field + field = { + name: fieldName!, + type: (dataViewField?.esTypes ? dataViewField.esTypes[0] : 'keyword') as RuntimeType, + customLabel: dataViewField.customLabel, + popularity: dataViewField.count, + format: indexPattern.getFormatterForFieldNoDefault(fieldName!)?.toJSON(), + }; + } } overlayRef = overlays.openFlyout( diff --git a/src/plugins/index_pattern_field_editor/public/shared_imports.ts b/src/plugins/index_pattern_field_editor/public/shared_imports.ts index bb332a7c0ded3..c99ee5e0dc06f 100644 --- a/src/plugins/index_pattern_field_editor/public/shared_imports.ts +++ b/src/plugins/index_pattern_field_editor/public/shared_imports.ts @@ -13,10 +13,10 @@ export { UsageCollectionStart } from '../../usage_collection/public'; export { RuntimeType, RuntimeField, + RuntimeFieldSpec, + RuntimeFieldSubField, KBN_FIELD_TYPES, ES_FIELD_TYPES, - EnhancedRuntimeField, - RuntimeCompositeWithSubFields, } from '../../data/common'; export type { SerializedFieldFormat } from '../../expressions/public'; diff --git a/src/plugins/index_pattern_field_editor/public/types.ts b/src/plugins/index_pattern_field_editor/public/types.ts index ed0f149d69192..ad1b65bedf27f 100644 --- a/src/plugins/index_pattern_field_editor/public/types.ts +++ b/src/plugins/index_pattern_field_editor/public/types.ts @@ -8,11 +8,7 @@ import { FunctionComponent } from 'react'; -import { - DataPublicPluginStart, - UsageCollectionStart, - EnhancedRuntimeField, -} from './shared_imports'; +import { DataPublicPluginStart, UsageCollectionStart, RuntimeField } from './shared_imports'; import { OpenFieldEditorOptions } from './open_editor'; import { OpenFieldDeleteModalOptions } from './open_delete_modal'; import { FormatEditorServiceSetup, FormatEditorServiceStart } from './service'; @@ -42,7 +38,7 @@ export interface StartPlugins { export type InternalFieldType = 'concrete' | 'runtime'; -export interface Field extends EnhancedRuntimeField { +export interface Field extends RuntimeField { name: string; } From 5462209e95889722aab538e4de05b3a5bf265c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 6 Sep 2021 15:45:19 +0100 Subject: [PATCH 54/60] Update server side code --- .../routes/create_index_pattern.ts | 2 -- .../runtime_fields/create_runtime_field.ts | 16 +++++----- .../runtime_fields/delete_runtime_field.ts | 8 ++--- .../runtime_fields/get_runtime_field.ts | 29 +++++++++++++++---- .../runtime_fields/put_runtime_field.ts | 27 ++++++++--------- .../runtime_fields/update_runtime_field.ts | 16 +++++----- .../routes/update_index_pattern.ts | 3 +- .../index_patterns/routes/util/schemas.ts | 23 ++++++++++----- 8 files changed, 74 insertions(+), 50 deletions(-) diff --git a/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts b/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts index 06dd09a641ede..7049903f84e8c 100644 --- a/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts +++ b/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts @@ -12,7 +12,6 @@ import { handleErrors } from './util/handle_errors'; import { fieldSpecSchema, runtimeFieldSpecSchema, - runtimeCompositeSpecSchema, serializedFieldFormatSchema, } from './util/schemas'; import { IRouter, StartServicesAccessor } from '../../../../../core/server'; @@ -45,7 +44,6 @@ const indexPatternSpecSchema = schema.object({ ), allowNoIndex: schema.maybe(schema.boolean()), runtimeFieldMap: schema.maybe(schema.recordOf(schema.string(), runtimeFieldSpecSchema)), - runtimeCompositeMap: schema.maybe(schema.recordOf(schema.string(), runtimeCompositeSpecSchema)), }); export const registerCreateIndexPatternRoute = ( diff --git a/src/plugins/data/server/index_patterns/routes/runtime_fields/create_runtime_field.ts b/src/plugins/data/server/index_patterns/routes/runtime_fields/create_runtime_field.ts index faf6d87b6d10b..9854e5f79c226 100644 --- a/src/plugins/data/server/index_patterns/routes/runtime_fields/create_runtime_field.ts +++ b/src/plugins/data/server/index_patterns/routes/runtime_fields/create_runtime_field.ts @@ -7,6 +7,7 @@ */ import { schema } from '@kbn/config-schema'; +import { RuntimeField } from '../../../../common'; import { handleErrors } from '../util/handle_errors'; import { runtimeFieldSpecSchema } from '../util/schemas'; import { IRouter, StartServicesAccessor } from '../../../../../../core/server'; @@ -53,19 +54,18 @@ export const registerCreateRuntimeFieldRoute = ( throw new Error(`Field [name = ${name}] already exists.`); } - indexPattern.addRuntimeField(name, runtimeField); - - const addedField = indexPattern.fields.getByName(name); - if (!addedField) throw new Error(`Could not create a field [name = ${name}].`); + const createdFields = indexPattern.addRuntimeField(name, runtimeField as RuntimeField); await indexPatternsService.updateSavedObject(indexPattern); - const savedField = indexPattern.fields.getByName(name); - if (!savedField) throw new Error(`Could not create a field [name = ${name}].`); - return res.ok({ body: { - field: savedField.toSpec(), + // New API for 7.16 & 8.x. Return an Array of DataViewFields created + fields: createdFields.map((f) => f.toSpec()), + // @deprecated + // To avoid creating a breaking change in 7.16 we continue to support + // the old "field" in the response + field: createdFields[0].toSpec(), index_pattern: indexPattern.toSpec(), }, }); diff --git a/src/plugins/data/server/index_patterns/routes/runtime_fields/delete_runtime_field.ts b/src/plugins/data/server/index_patterns/routes/runtime_fields/delete_runtime_field.ts index 58b8529d7cf5a..b2c75e14a3a8b 100644 --- a/src/plugins/data/server/index_patterns/routes/runtime_fields/delete_runtime_field.ts +++ b/src/plugins/data/server/index_patterns/routes/runtime_fields/delete_runtime_field.ts @@ -44,16 +44,12 @@ export const registerDeleteRuntimeFieldRoute = ( const name = req.params.name; const indexPattern = await indexPatternsService.get(id); - const field = indexPattern.fields.getByName(name); + const runtimeField = indexPattern.getRuntimeField(name); - if (!field) { + if (!runtimeField) { throw new ErrorIndexPatternFieldNotFound(id, name); } - if (!field.runtimeField) { - throw new Error('Only runtime fields can be deleted.'); - } - indexPattern.removeRuntimeField(name); await indexPatternsService.updateSavedObject(indexPattern); diff --git a/src/plugins/data/server/index_patterns/routes/runtime_fields/get_runtime_field.ts b/src/plugins/data/server/index_patterns/routes/runtime_fields/get_runtime_field.ts index 6bc2bf396c0b4..f5b2b29648313 100644 --- a/src/plugins/data/server/index_patterns/routes/runtime_fields/get_runtime_field.ts +++ b/src/plugins/data/server/index_patterns/routes/runtime_fields/get_runtime_field.ts @@ -7,6 +7,7 @@ */ import { schema } from '@kbn/config-schema'; +import { DataViewField } from '../../../../common'; import { ErrorIndexPatternFieldNotFound } from '../../error'; import { handleErrors } from '../util/handle_errors'; import { IRouter, StartServicesAccessor } from '../../../../../../core/server'; @@ -46,20 +47,36 @@ export const registerGetRuntimeFieldRoute = ( const indexPattern = await indexPatternsService.get(id); - const field = indexPattern.fields.getByName(name); + const runtimeField = indexPattern.getRuntimeField(name); - if (!field) { + if (!runtimeField) { throw new ErrorIndexPatternFieldNotFound(id, name); } - if (!field.runtimeField) { - throw new Error('Only runtime fields can be retrieved.'); + // Access the data view fields created for the runtime field + let dataViewFields: DataViewField[]; + + if (runtimeField.type === 'composite') { + // For "composite" runtime fields we need to look at the "fields" + dataViewFields = Object.keys(runtimeField.fields!) + .map((subFieldName) => { + const fullName = `${name}.${subFieldName}`; + return indexPattern.fields.getByName(fullName); + }) + .filter(Boolean) as DataViewField[]; + } else { + dataViewFields = [indexPattern.fields.getByName(name)].filter(Boolean) as DataViewField[]; } return res.ok({ body: { - field: field.toSpec(), - runtimeField: indexPattern.getRuntimeField(name), + // New API for 7.16 & 8.x. Return an Array of DataViewFields for the runtime field + fields: dataViewFields, + // @deprecated + // To avoid creating a breaking change in 7.16 we continue to support + // the old "field" in the response + field: dataViewFields[0].toSpec(), + runtimeField, }, }); }) diff --git a/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts b/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts index f14e9262de9ae..6395e2b65111c 100644 --- a/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts +++ b/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts @@ -7,7 +7,8 @@ */ import { schema } from '@kbn/config-schema'; -import { EnhancedRuntimeField } from 'src/plugins/data/common'; +import { RuntimeField } from 'src/plugins/data/common'; +import { ErrorIndexPatternFieldNotFound } from '../../error'; import { handleErrors } from '../util/handle_errors'; import { runtimeFieldSpecSchema } from '../util/schemas'; import { IRouter, StartServicesAccessor } from '../../../../../../core/server'; @@ -47,31 +48,31 @@ export const registerPutRuntimeFieldRoute = ( const id = req.params.id; const { name, runtimeField } = req.body as { name: string; - runtimeField: EnhancedRuntimeField; + runtimeField: RuntimeField; }; const indexPattern = await indexPatternsService.get(id); - const oldFieldObject = indexPattern.fields.getByName(name); + const oldRuntimeFieldObject = indexPattern.getRuntimeField(name); - if (oldFieldObject && !oldFieldObject.runtimeField) { - throw new Error('Only runtime fields can be updated'); + if (!oldRuntimeFieldObject) { + throw new ErrorIndexPatternFieldNotFound(id, name); } - if (oldFieldObject) { - indexPattern.removeRuntimeField(name); - } + indexPattern.removeRuntimeField(name); - indexPattern.addRuntimeField(name, runtimeField); + const createdFields = indexPattern.addRuntimeField(name, runtimeField); await indexPatternsService.updateSavedObject(indexPattern); - const fieldObject = indexPattern.fields.getByName(name); - if (!fieldObject) throw new Error(`Could not create a field [name = ${name}].`); - return res.ok({ body: { - field: fieldObject.toSpec(), + // New API for 7.16 & 8.x. Return an Array of DataViewFields created + fields: createdFields.map((f) => f.toSpec()), + // @deprecated + // To avoid creating a breaking change in 7.16 we continue to support + // the old "field" in the response + field: createdFields[0].toSpec(), index_pattern: indexPattern.toSpec(), }, }); diff --git a/src/plugins/data/server/index_patterns/routes/runtime_fields/update_runtime_field.ts b/src/plugins/data/server/index_patterns/routes/runtime_fields/update_runtime_field.ts index 601d4653a54bf..dd2a2fee91049 100644 --- a/src/plugins/data/server/index_patterns/routes/runtime_fields/update_runtime_field.ts +++ b/src/plugins/data/server/index_patterns/routes/runtime_fields/update_runtime_field.ts @@ -7,7 +7,7 @@ */ import { schema } from '@kbn/config-schema'; -import { EnhancedRuntimeField } from 'src/plugins/data/common'; +import { RuntimeField } from 'src/plugins/data/common'; import { ErrorIndexPatternFieldNotFound } from '../../error'; import { handleErrors } from '../util/handle_errors'; import { runtimeFieldSpec, runtimeFieldSpecTypeSchema } from '../util/schemas'; @@ -53,7 +53,7 @@ export const registerUpdateRuntimeFieldRoute = ( ); const id = req.params.id; const name = req.params.name; - const runtimeField = req.body.runtimeField as Partial; + const runtimeField = req.body.runtimeField as Partial; const indexPattern = await indexPatternsService.get(id); const existingRuntimeField = indexPattern.getRuntimeField(name); @@ -63,19 +63,21 @@ export const registerUpdateRuntimeFieldRoute = ( } indexPattern.removeRuntimeField(name); - indexPattern.addRuntimeField(name, { + const createdFields = indexPattern.addRuntimeField(name, { ...existingRuntimeField, ...runtimeField, }); await indexPatternsService.updateSavedObject(indexPattern); - const fieldObject = indexPattern.fields.getByName(name); - if (!fieldObject) throw new Error(`Could not create a field [name = ${name}].`); - return res.ok({ body: { - field: fieldObject.toSpec(), + // New API for 7.16 & 8.x. Return an Array of DataViewFields created + fields: createdFields.map((f) => f.toSpec()), + // @deprecated + // To avoid creating a breaking change in 7.16 we continue to support + // the old "field" in the response + field: createdFields[0].toSpec(), index_pattern: indexPattern.toSpec(), }, }); diff --git a/src/plugins/data/server/index_patterns/routes/update_index_pattern.ts b/src/plugins/data/server/index_patterns/routes/update_index_pattern.ts index 1c88550c154c5..8936d45ba4291 100644 --- a/src/plugins/data/server/index_patterns/routes/update_index_pattern.ts +++ b/src/plugins/data/server/index_patterns/routes/update_index_pattern.ts @@ -14,6 +14,7 @@ import { serializedFieldFormatSchema, } from './util/schemas'; import { IRouter, StartServicesAccessor } from '../../../../../core/server'; +import { RuntimeField } from '../../../common'; import type { DataPluginStart, DataPluginStartDependencies } from '../../plugin'; const indexPatternUpdateSchema = schema.object({ @@ -139,7 +140,7 @@ export const registerUpdateIndexPatternRoute = ( if (runtimeFieldMap !== undefined) { changeCount++; - indexPattern.replaceAllRuntimeFields(runtimeFieldMap); + indexPattern.replaceAllRuntimeFields(runtimeFieldMap as Record); } if (changeCount < 1) { diff --git a/src/plugins/data/server/index_patterns/routes/util/schemas.ts b/src/plugins/data/server/index_patterns/routes/util/schemas.ts index a3a3cf5afbceb..f378832254047 100644 --- a/src/plugins/data/server/index_patterns/routes/util/schemas.ts +++ b/src/plugins/data/server/index_patterns/routes/util/schemas.ts @@ -72,6 +72,21 @@ export const runtimeFieldSpec = { source: schema.string(), }) ), + fields: schema.maybe( + schema.recordOf( + schema.string(), + schema.object({ + type: runtimeFieldSpecTypeSchema, + format: schema.maybe(serializedFieldFormatSchema), + customLabel: schema.maybe(schema.string()), + popularity: schema.maybe( + schema.number({ + min: 0, + }) + ), + }) + ) + ), format: schema.maybe(serializedFieldFormatSchema), customLabel: schema.maybe(schema.string()), popularity: schema.maybe( @@ -80,11 +95,5 @@ export const runtimeFieldSpec = { }) ), }; -export const runtimeFieldSpecSchema = schema.object(runtimeFieldSpec); -export const runtimeCompositeSpec = { - name: schema.string(), - script: runtimeFieldSpec.script, - fields: schema.arrayOf(schema.string()), -}; -export const runtimeCompositeSpecSchema = schema.object(runtimeCompositeSpec); +export const runtimeFieldSpecSchema = schema.object(runtimeFieldSpec); From 31d735a9627a470e96d5da1fcfca7308691345fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 6 Sep 2021 15:57:57 +0100 Subject: [PATCH 55/60] Fix tests --- .../index_patterns/__snapshots__/index_patterns.test.ts.snap | 1 - .../discover_index_pattern_management.test.tsx.snap | 1 - .../client_integration/field_editor_flyout_content.test.ts | 4 +++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap index e348c10a5a482..af9499bd7e263 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap +++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap @@ -37,7 +37,6 @@ Object { "fields": Object {}, "id": "id", "intervalName": undefined, - "runtimeCompositeMap": Object {}, "runtimeFieldMap": Object { "aRuntimeField": Object { "script": Object { diff --git a/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap b/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap index 06adad0b51e2b..3ad902ed22fe8 100644 --- a/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap +++ b/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap @@ -664,7 +664,6 @@ exports[`Discover IndexPattern Management renders correctly 1`] = ` ], "originalSavedObjectBody": Object {}, "resetOriginalSavedObjectBody": [Function], - "runtimeCompositeMap": Object {}, "runtimeFieldMap": Object {}, "setFieldFormat": [Function], "shortDotsEnable": false, diff --git a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts index 35ffb7f58b6c7..75ac0b75c444f 100644 --- a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts +++ b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts @@ -69,7 +69,7 @@ describe('', () => { expect(onSave).toHaveBeenCalled(); const fieldReturned = onSave.mock.calls[onSave.mock.calls.length - 1][0]; - expect(fieldReturned).toEqual(field); + expect(fieldReturned).toEqual({ ...field, format: null }); }); test('should accept an onCancel prop', async () => { @@ -135,6 +135,7 @@ describe('', () => { name: 'someName', type: 'keyword', // default to keyword script: { source: 'echo("hello")' }, + format: null, }); // Change the type and make sure it is forwarded @@ -150,6 +151,7 @@ describe('', () => { name: 'someName', type: 'other_type', script: { source: 'echo("hello")' }, + format: null, }); }); }); From 9f83024525515041861d29e6e54687a3e37c515a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 6 Sep 2021 16:43:05 +0100 Subject: [PATCH 56/60] Fix TS issue and API integration test --- .../runtime_fields/put_runtime_field.ts | 6 ++--- .../public/open_delete_modal.tsx | 6 +---- .../delete_runtime_field/errors.ts | 10 -------- .../get_runtime_field/errors.ts | 10 -------- .../put_runtime_field/errors.ts | 25 ------------------- 5 files changed, 3 insertions(+), 54 deletions(-) diff --git a/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts b/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts index 6395e2b65111c..6a5874315a409 100644 --- a/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts +++ b/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts @@ -55,12 +55,10 @@ export const registerPutRuntimeFieldRoute = ( const oldRuntimeFieldObject = indexPattern.getRuntimeField(name); - if (!oldRuntimeFieldObject) { - throw new ErrorIndexPatternFieldNotFound(id, name); + if (oldRuntimeFieldObject) { + indexPattern.removeRuntimeField(name); } - indexPattern.removeRuntimeField(name); - const createdFields = indexPattern.addRuntimeField(name, runtimeField); await indexPatternsService.updateSavedObject(indexPattern); diff --git a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx index 2620f65d2d568..06018c010b1c9 100644 --- a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx @@ -42,11 +42,7 @@ export const getFieldDeleteModalOpener = ({ }: Dependencies) => (options: OpenFieldDeleteModalOptions): CloseEditor => { if (typeof options.fieldName === 'string') { const fieldToDelete = options.ctx.indexPattern.getFieldByName(options.fieldName); - const parentComposite = fieldToDelete?.runtimeField?.parentComposite; - const doesBelongToCompositeField = - parentComposite === undefined - ? false - : options.ctx.indexPattern.getRuntimeComposite(parentComposite) !== null; + const doesBelongToCompositeField = fieldToDelete?.runtimeField?.type === 'composite'; if (doesBelongToCompositeField) { console.log( // eslint-disable-line diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/delete_runtime_field/errors.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/delete_runtime_field/errors.ts index b41a630889ff8..6f5ff46816bb5 100644 --- a/test/api_integration/apis/index_patterns/runtime_fields_crud/delete_runtime_field/errors.ts +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/delete_runtime_field/errors.ts @@ -56,16 +56,6 @@ export default function ({ getService }: FtrProviderContext) { expect(response1.status).to.be(404); }); - it('returns error when attempting to delete a field which is not a runtime field', async () => { - const response2 = await supertest.delete( - `/api/index_patterns/index_pattern/${indexPattern.id}/runtime_field/foo` - ); - - expect(response2.status).to.be(400); - expect(response2.body.statusCode).to.be(400); - expect(response2.body.message).to.be('Only runtime fields can be deleted.'); - }); - it('returns error when ID is too long', async () => { const id = `xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx`; const response = await supertest.delete( diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/get_runtime_field/errors.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/get_runtime_field/errors.ts index 3608089e4641a..d17e62630539e 100644 --- a/test/api_integration/apis/index_patterns/runtime_fields_crud/get_runtime_field/errors.ts +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/get_runtime_field/errors.ts @@ -67,15 +67,5 @@ export default function ({ getService }: FtrProviderContext) { '[request params.id]: value has length [1759] but it must have a maximum length of [1000].' ); }); - - it('returns error when attempting to fetch a field which is not a runtime field', async () => { - const response2 = await supertest.get( - `/api/index_patterns/index_pattern/${indexPattern.id}/runtime_field/foo` - ); - - expect(response2.status).to.be(400); - expect(response2.body.statusCode).to.be(400); - expect(response2.body.message).to.be('Only runtime fields can be retrieved.'); - }); }); } diff --git a/test/api_integration/apis/index_patterns/runtime_fields_crud/put_runtime_field/errors.ts b/test/api_integration/apis/index_patterns/runtime_fields_crud/put_runtime_field/errors.ts index 9faca08238033..0741f1988c647 100644 --- a/test/api_integration/apis/index_patterns/runtime_fields_crud/put_runtime_field/errors.ts +++ b/test/api_integration/apis/index_patterns/runtime_fields_crud/put_runtime_field/errors.ts @@ -40,30 +40,5 @@ export default function ({ getService }: FtrProviderContext) { expect(response.status).to.be(404); }); - - it('returns error on non-runtime field update attempt', async () => { - const title = `basic_index`; - const response1 = await supertest.post('/api/index_patterns/index_pattern').send({ - override: true, - index_pattern: { - title, - }, - }); - - const response2 = await supertest - .put(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/runtime_field`) - .send({ - name: 'bar', - runtimeField: { - type: 'long', - script: { - source: "emit(doc['field_name'].value)", - }, - }, - }); - - expect(response2.status).to.be(400); - expect(response2.body.message).to.be('Only runtime fields can be updated'); - }); }); } From dd43053d1b0e232e2b9c0a416e4b85b24e933fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 6 Sep 2021 16:53:41 +0100 Subject: [PATCH 57/60] Fix TS issue --- .../index_patterns/routes/runtime_fields/put_runtime_field.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts b/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts index 6a5874315a409..1397c25d12068 100644 --- a/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts +++ b/src/plugins/data/server/index_patterns/routes/runtime_fields/put_runtime_field.ts @@ -8,7 +8,6 @@ import { schema } from '@kbn/config-schema'; import { RuntimeField } from 'src/plugins/data/common'; -import { ErrorIndexPatternFieldNotFound } from '../../error'; import { handleErrors } from '../util/handle_errors'; import { runtimeFieldSpecSchema } from '../util/schemas'; import { IRouter, StartServicesAccessor } from '../../../../../../core/server'; From f454c1635bb9d989fa8da6b8a592c4ea8cb51f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Mon, 6 Sep 2021 19:07:55 +0100 Subject: [PATCH 58/60] Fix tests --- .../index_patterns/__snapshots__/index_pattern.test.ts.snap | 1 - .../__jest__/client_integration/field_editor.test.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap index 3ad1c44c56eb4..1c6b57f70071b 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap +++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap @@ -776,7 +776,6 @@ Object { }, "id": "test-pattern", "intervalName": undefined, - "runtimeCompositeMap": Object {}, "runtimeFieldMap": Object { "runtime_field": Object { "script": Object { diff --git a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx index a7f990bf8259d..73a0f6d997de0 100644 --- a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx +++ b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx @@ -112,7 +112,7 @@ describe('', () => { expect(lastState.submit).toBeDefined(); const { data: formData } = await submitFormAndGetData(lastState); - expect(formData).toEqual(field); + expect(formData).toEqual({ ...field, format: null }); // Make sure that both isValid and isSubmitted state are now "true" lastState = getLastStateUpdate(); From 0339cddd9253c32acb6e1d8550251de198403959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Tue, 7 Sep 2021 10:06:49 +0100 Subject: [PATCH 59/60] Move logic to add composite runtime field to its own method --- .../index_patterns/index_pattern.ts | 177 ++++++++++-------- 1 file changed, 96 insertions(+), 81 deletions(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 1cdb84d238cf2..52a0833649206 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -367,95 +367,24 @@ export class DataView implements IIndexPattern { * @param runtimeField Runtime field definition */ addRuntimeField(name: string, runtimeField: RuntimeField): DataViewField[] { - const { type, script, fields, customLabel, format, popularity } = runtimeField; + const { type, script, customLabel, format, popularity } = runtimeField; - if (type === 'composite' && (fields === undefined || Object.keys(fields).length === 0)) { - throw new Error(`Can't add composite runtime field [name = ${name}] without subfields.`); - } - - let runtimeFieldSpecFields: RuntimeFieldSpec['fields'] | undefined; - - if (fields !== undefined) { - runtimeFieldSpecFields = Object.entries(fields).reduce( - (acc, [subFieldName, subField]) => { - return { - ...acc, - [subFieldName]: { - type: subField.type, - }, - }; - }, - {} - ); + if (type === 'composite') { + return this.addCompositeRuntimeField(name, runtimeField); } const runtimeFieldSpec: RuntimeFieldSpec = { type, script, - fields: runtimeFieldSpecFields, - }; - - const updateOrCreateField = ( - fieldName: string, - fieldType: RuntimeType, - config: FieldConfiguration - ): DataViewField => { - // Create the field if it does not exist or update an existing one - let createdField: DataViewField | undefined; - const existingField = this.getFieldByName(fieldName); - - if (existingField) { - existingField.runtimeField = runtimeFieldSpec; - } else { - createdField = this.fields.add({ - name: fieldName, - runtimeField: runtimeFieldSpec, - type: castEsToKbnFieldTypeName(fieldType), - aggregatable: true, - searchable: true, - count: config.popularity ?? 0, - readFromDocValues: false, - }); - } - - // Apply configuration to the field - this.setFieldCustomLabel(fieldName, config.customLabel); - if (config.format) { - this.setFieldFormat(fieldName, config.format); - } else if (config.format === null) { - this.deleteFieldFormat(fieldName); - } - - return existingField ?? createdField!; }; - let dataViewFields: DataViewField[]; - - if (type === 'composite') { - // Make sure no field with the same name already exist - if (this.getFieldByName(name) !== undefined) { - throw new Error( - `Can't create composite runtime field ["${name}"] as there is already a field with this name` - ); - } - - // We first remove the runtime composite field with the same name which will remove all of its subFields. - // This guarantees that we don't leave behind orphan data view fields - this.removeRuntimeField(name); - - // For composite runtime field we don't add those to the field list as - // composite runtime fields are **not** fields but **holder** of fields. - // What we do add to the field list are all **their subFields**. - dataViewFields = Object.entries(fields!).map(([subFieldName, subField]) => - updateOrCreateField(`${name}.${subFieldName}`, subField.type, { - customLabel: subField.customLabel, - format: subField.format, - popularity: subField.popularity, - }) - ); - } else { - dataViewFields = [updateOrCreateField(name, type, { customLabel, format, popularity })]; - } + const dataViewFields = [ + this.updateOrAddRuntimeField(name, type, runtimeFieldSpec, { + customLabel, + format, + popularity, + }), + ]; this.runtimeFieldMap[name] = runtimeFieldSpec; return dataViewFields; @@ -637,6 +566,92 @@ export class DataView implements IIndexPattern { public readonly deleteFieldFormat = (fieldName: string) => { delete this.fieldFormatMap[fieldName]; }; + + private addCompositeRuntimeField(name: string, runtimeField: RuntimeField): DataViewField[] { + const { type, script, fields } = runtimeField; + + // Make sure subFields are provided + if (fields === undefined || Object.keys(fields).length === 0) { + throw new Error(`Can't add composite runtime field [name = ${name}] without subfields.`); + } + + // Make sure no field with the same name already exist + if (this.getFieldByName(name) !== undefined) { + throw new Error( + `Can't create composite runtime field ["${name}"] as there is already a field with this name` + ); + } + + const runtimeFieldSpecFields: RuntimeFieldSpec['fields'] = Object.entries(fields).reduce< + RuntimeFieldSpec['fields'] + >((acc, [subFieldName, subField]) => { + return { + ...acc, + [subFieldName]: { + type: subField.type, + }, + }; + }, {}); + + const runtimeFieldSpec: RuntimeFieldSpec = { + type, + script, + fields: runtimeFieldSpecFields, + }; + + // We first remove the runtime composite field with the same name which will remove all of its subFields. + // This guarantees that we don't leave behind orphan data view fields + this.removeRuntimeField(name); + + // We don't add composite runtime fields to the field list as + // they are not fields but **holder** of fields. + // What we do add to the field list are all their subFields. + const dataViewFields = Object.entries(fields).map(([subFieldName, subField]) => + this.updateOrAddRuntimeField(`${name}.${subFieldName}`, subField.type, runtimeFieldSpec, { + customLabel: subField.customLabel, + format: subField.format, + popularity: subField.popularity, + }) + ); + + this.runtimeFieldMap[name] = runtimeFieldSpec; + return dataViewFields; + } + + private updateOrAddRuntimeField( + fieldName: string, + fieldType: RuntimeType, + runtimeFieldSpec: RuntimeFieldSpec, + config: FieldConfiguration + ): DataViewField { + // Create the field if it does not exist or update an existing one + let createdField: DataViewField | undefined; + const existingField = this.getFieldByName(fieldName); + + if (existingField) { + existingField.runtimeField = runtimeFieldSpec; + } else { + createdField = this.fields.add({ + name: fieldName, + runtimeField: runtimeFieldSpec, + type: castEsToKbnFieldTypeName(fieldType), + aggregatable: true, + searchable: true, + count: config.popularity ?? 0, + readFromDocValues: false, + }); + } + + // Apply configuration to the field + this.setFieldCustomLabel(fieldName, config.customLabel); + if (config.format) { + this.setFieldFormat(fieldName, config.format); + } else if (config.format === null) { + this.deleteFieldFormat(fieldName); + } + + return createdField ?? existingField!; + } } /** From d4a75dc7bc4b29731efb1671f07c41e0a63b99fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20Loix?= Date: Wed, 8 Sep 2021 15:36:54 +0100 Subject: [PATCH 60/60] Remove dependency on expressions plugin --- src/plugins/index_pattern_field_editor/kibana.json | 2 +- .../index_pattern_field_editor/public/shared_imports.ts | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/plugins/index_pattern_field_editor/kibana.json b/src/plugins/index_pattern_field_editor/kibana.json index 7275344f04286..898e7c564e57f 100644 --- a/src/plugins/index_pattern_field_editor/kibana.json +++ b/src/plugins/index_pattern_field_editor/kibana.json @@ -3,7 +3,7 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["data", "expressions"], + "requiredPlugins": ["data"], "optionalPlugins": ["usageCollection"], "requiredBundles": ["kibanaReact", "esUiShared", "usageCollection", "fieldFormats"], "owner": { diff --git a/src/plugins/index_pattern_field_editor/public/shared_imports.ts b/src/plugins/index_pattern_field_editor/public/shared_imports.ts index c99ee5e0dc06f..c1ad6275b7599 100644 --- a/src/plugins/index_pattern_field_editor/public/shared_imports.ts +++ b/src/plugins/index_pattern_field_editor/public/shared_imports.ts @@ -19,11 +19,9 @@ export { ES_FIELD_TYPES, } from '../../data/common'; -export type { SerializedFieldFormat } from '../../expressions/public'; - export { createKibanaReactContext, toMountPoint, CodeEditor } from '../../kibana_react/public'; -export { FieldFormat } from '../../field_formats/common'; +export { FieldFormat, SerializedFieldFormat } from '../../field_formats/common'; export { useForm,