diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx index 4a79f30a17a05..8291b673cd17a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx @@ -87,36 +87,42 @@ const initialState: IndexPatternPrivateState = { fields: [ { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, }, { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, }, { name: 'memory', + displayName: 'amemory', type: 'number', aggregatable: true, searchable: true, }, { name: 'unsupported', + displayName: 'unsupported', type: 'geo', aggregatable: true, searchable: true, }, { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, }, { name: 'client', + displayName: 'client', type: 'ip', aggregatable: true, searchable: true, @@ -131,6 +137,7 @@ const initialState: IndexPatternPrivateState = { fields: [ { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, @@ -145,6 +152,7 @@ const initialState: IndexPatternPrivateState = { }, { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, @@ -166,6 +174,7 @@ const initialState: IndexPatternPrivateState = { }, { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, @@ -185,18 +194,21 @@ const initialState: IndexPatternPrivateState = { fields: [ { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, }, { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, }, { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, @@ -581,18 +593,19 @@ describe('IndexPattern Data Panel', () => { .find('[data-test-subj="lnsIndexPatternAvailableFields"]') .find(FieldItem) .map((fieldItem) => fieldItem.prop('field').name) - ).toEqual(['bytes', 'memory']); + ).toEqual(['memory', 'bytes']); wrapper .find('[data-test-subj="lnsIndexPatternEmptyFields"]') .find('button') .first() .simulate('click'); + const emptyAccordion = wrapper.find('[data-test-subj="lnsIndexPatternEmptyFields"]'); expect( - wrapper - .find('[data-test-subj="lnsIndexPatternEmptyFields"]') - .find(FieldItem) - .map((fieldItem) => fieldItem.prop('field').name) + emptyAccordion.find(FieldItem).map((fieldItem) => fieldItem.prop('field').name) ).toEqual(['client', 'source', 'timestamp']); + expect( + emptyAccordion.find(FieldItem).map((fieldItem) => fieldItem.prop('field').displayName) + ).toEqual(['client', 'source', 'timestampLabel']); }); it('should display NoFieldsCallout when all fields are empty', async () => { @@ -615,8 +628,8 @@ describe('IndexPattern Data Panel', () => { wrapper .find('[data-test-subj="lnsIndexPatternEmptyFields"]') .find(FieldItem) - .map((fieldItem) => fieldItem.prop('field').name) - ).toEqual(['bytes', 'client', 'memory', 'source', 'timestamp']); + .map((fieldItem) => fieldItem.prop('field').displayName) + ).toEqual(['amemory', 'bytes', 'client', 'source', 'timestampLabel']); }); it('should display spinner for available fields accordion if existing fields are not loaded yet', async () => { @@ -656,10 +669,9 @@ describe('IndexPattern Data Panel', () => { wrapper.find('[data-test-subj="typeFilter-number"]').first().simulate('click'); - expect(wrapper.find(FieldItem).map((fieldItem) => fieldItem.prop('field').name)).toEqual([ - 'bytes', - 'memory', - ]); + expect( + wrapper.find(FieldItem).map((fieldItem) => fieldItem.prop('field').displayName) + ).toEqual(['amemory', 'bytes']); }); it('should display no fields in groups when filtered by type Record', () => { @@ -686,14 +698,9 @@ describe('IndexPattern Data Panel', () => { .find('button') .first() .simulate('click'); - expect(wrapper.find(FieldItem).map((fieldItem) => fieldItem.prop('field').name)).toEqual([ - 'Records', - 'bytes', - 'memory', - 'client', - 'source', - 'timestamp', - ]); + expect( + wrapper.find(FieldItem).map((fieldItem) => fieldItem.prop('field').displayName) + ).toEqual(['Records', 'amemory', 'bytes', 'client', 'source', 'timestampLabel']); }); it('should filter down by type and by name', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx index bdcce52314634..0777b9b9d8e57 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx @@ -59,7 +59,7 @@ const FixedEuiContextMenuPanel = (EuiContextMenuPanel as unknown) as React.Funct >; function sortFields(fieldA: IndexPatternField, fieldB: IndexPatternField) { - return fieldA.name.localeCompare(fieldB.name, undefined, { sensitivity: 'base' }); + return fieldA.displayName.localeCompare(fieldB.displayName, undefined, { sensitivity: 'base' }); } const supportedFieldTypes = new Set(['string', 'number', 'boolean', 'date', 'ip', 'document']); @@ -323,7 +323,8 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ fieldGroup.filter((field) => { if ( localState.nameFilter.length && - !field.name.toLowerCase().includes(localState.nameFilter.toLowerCase()) + !field.name.toLowerCase().includes(localState.nameFilter.toLowerCase()) && + !field.displayName.toLowerCase().includes(localState.nameFilter.toLowerCase()) ) { return false; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/bucket_nesting_editor.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/bucket_nesting_editor.test.tsx index c6dbb6f617acf..3d6c9f6047c81 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/bucket_nesting_editor.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/bucket_nesting_editor.test.tsx @@ -8,6 +8,13 @@ import { mount } from 'enzyme'; import React from 'react'; import { BucketNestingEditor } from './bucket_nesting_editor'; import { IndexPatternColumn } from '../indexpattern'; +import { IndexPatternField } from '../types'; + +const fieldMap = { + a: { displayName: 'a' } as IndexPatternField, + b: { displayName: 'b' } as IndexPatternField, + c: { displayName: 'c' } as IndexPatternField, +}; describe('BucketNestingEditor', () => { function mockCol(col: Partial = {}): IndexPatternColumn { @@ -32,6 +39,7 @@ describe('BucketNestingEditor', () => { it('should display the top level grouping when at the root', () => { const component = mount( { const component = mount( { const component = mount( { const component = mount( { const component = mount( { const component = mount( { const component = mount( { const component = mount( { const setColumns = jest.fn(); const component = mount( void; + fieldMap: Record; }) { const column = layer.columns[columnId]; const columns = Object.entries(layer.columns); @@ -37,14 +39,14 @@ export function BucketNestingEditor({ .map(([value, c]) => ({ value, text: c.label, - fieldName: hasField(c) ? c.sourceField : '', + fieldName: hasField(c) ? fieldMap[c.sourceField].displayName : '', })); if (!column || !column.isBucketed || !aggColumns.length) { return null; } - const fieldName = hasField(column) ? column.sourceField : ''; + const fieldName = hasField(column) ? fieldMap[column.sourceField].displayName : ''; const prevColumn = layer.columnOrder[layer.columnOrder.indexOf(columnId) - 1]; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 1f48f95ee45e0..bca179b437521 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -45,6 +45,7 @@ const expectedIndexPatterns = { fields: [ { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, @@ -52,6 +53,7 @@ const expectedIndexPatterns = { }, { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, @@ -59,6 +61,7 @@ const expectedIndexPatterns = { }, { name: 'memory', + displayName: 'memory', type: 'number', aggregatable: true, searchable: true, @@ -66,6 +69,7 @@ const expectedIndexPatterns = { }, { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, @@ -210,9 +214,8 @@ describe('IndexPatternDimensionEditorPanel', () => { expect(options).toHaveLength(2); expect(options![0].label).toEqual('Records'); - expect(options![1].options!.map(({ label }) => label)).toEqual([ - 'timestamp', + 'timestampLabel', 'bytes', 'memory', 'source', @@ -239,7 +242,7 @@ describe('IndexPatternDimensionEditorPanel', () => { .filter('[data-test-subj="indexPattern-dimension-field"]') .prop('options'); - expect(options![1].options!.map(({ label }) => label)).toEqual(['timestamp', 'source']); + expect(options![1].options!.map(({ label }) => label)).toEqual(['timestampLabel', 'source']); }); it('should indicate fields which are incompatible for the operation of the current column', () => { @@ -277,7 +280,7 @@ describe('IndexPatternDimensionEditorPanel', () => { expect(options![0]['data-test-subj']).toEqual('lns-fieldOptionIncompatible-Records'); expect( - options![1].options!.filter(({ label }) => label === 'timestamp')[0]['data-test-subj'] + options![1].options!.filter(({ label }) => label === 'timestampLabel')[0]['data-test-subj'] ).toContain('Incompatible'); expect( options![1].options!.filter(({ label }) => label === 'memory')[0]['data-test-subj'] @@ -651,7 +654,9 @@ describe('IndexPatternDimensionEditorPanel', () => { expect(options![0]['data-test-subj']).toContain('Incompatible'); expect( - options![1].options!.filter(({ label }) => label === 'timestamp')[0]['data-test-subj'] + options![1].options!.filter(({ label }) => label === 'timestampLabel')[0][ + 'data-test-subj' + ] ).toContain('Incompatible'); expect( options![1].options!.filter(({ label }) => label === 'source')[0]['data-test-subj'] @@ -769,7 +774,9 @@ describe('IndexPatternDimensionEditorPanel', () => { expect(options![0]['data-test-subj']).toContain('Incompatible'); expect( - options![1].options!.filter(({ label }) => label === 'timestamp')[0]['data-test-subj'] + options![1].options!.filter(({ label }) => label === 'timestampLabel')[0][ + 'data-test-subj' + ] ).toContain('Incompatible'); expect( options![1].options!.filter(({ label }) => label === 'source')[0]['data-test-subj'] @@ -923,7 +930,7 @@ describe('IndexPatternDimensionEditorPanel', () => { expect(options![0]['data-test-subj']).toContain('Incompatible'); expect( - options![1].options!.filter(({ label }) => label === 'timestamp')[0]['data-test-subj'] + options![1].options!.filter(({ label }) => label === 'timestampLabel')[0]['data-test-subj'] ).toContain('Incompatible'); expect( options![1].options!.filter(({ label }) => label === 'bytes')[0]['data-test-subj'] @@ -1095,12 +1102,12 @@ describe('IndexPatternDimensionEditorPanel', () => { columnOrder: ['col1'], columns: { col1: { - label: 'Average of bar', + label: 'Average of memory', dataType: 'number', isBucketed: false, // Private operationType: 'avg', - sourceField: 'bar', + sourceField: 'memory', }, }, }, @@ -1145,12 +1152,12 @@ describe('IndexPatternDimensionEditorPanel', () => { columnOrder: ['col1'], columns: { col1: { - label: 'Average of bar', + label: 'Average of memory', dataType: 'number', isBucketed: false, // Private operationType: 'avg', - sourceField: 'bar', + sourceField: 'memory', params: { format: { id: 'bytes', params: { decimals: 0 } }, }, @@ -1195,12 +1202,12 @@ describe('IndexPatternDimensionEditorPanel', () => { columnOrder: ['col1'], columns: { col1: { - label: 'Average of bar', + label: 'Average of memory', dataType: 'number', isBucketed: false, // Private operationType: 'avg', - sourceField: 'bar', + sourceField: 'memory', params: { format: { id: 'bytes', params: { decimals: 2 } }, }, @@ -1253,12 +1260,14 @@ describe('IndexPatternDimensionEditorPanel', () => { { aggregatable: true, name: 'bar', + displayName: 'bar', searchable: true, type: 'number', }, { aggregatable: true, name: 'mystring', + displayName: 'mystring', searchable: true, type: 'string', }, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx index 4c85a55ad6011..b2a59788b50f9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx @@ -74,7 +74,7 @@ export function FieldSelect({ function fieldNamesToOptions(items: string[]) { return items .map((field) => ({ - label: field, + label: fieldMap[field].displayName, value: { type: 'field', field, @@ -105,7 +105,7 @@ export function FieldSelect({ // eslint-disable-next-line @typescript-eslint/naming-convention 'lnFieldSelect__option--nonExistant': !exists, }), - 'data-test-subj': `lns-fieldOption${compatible ? '' : 'Incompatible'}-${label}`, + 'data-test-subj': `lns-fieldOption${compatible ? '' : 'Incompatible'}-${value.field}`, })); } @@ -161,7 +161,7 @@ export function FieldSelect({ ? selectedColumnSourceField ? [ { - label: selectedColumnSourceField, + label: fieldMap[selectedColumnSourceField].displayName, value: { type: 'field', field: selectedColumnSourceField }, }, ] diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx index a5108b30cea1d..038b51b922286 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx @@ -378,6 +378,7 @@ export function PopoverEditor(props: PopoverEditorProps) { {!hideGrouping && ( { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/document_field.ts b/x-pack/plugins/lens/public/indexpattern_datasource/document_field.ts index e0a7f27835e42..b0c5540a6b94f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/document_field.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/document_field.ts @@ -5,12 +5,16 @@ */ import { i18n } from '@kbn/i18n'; +import { IndexPatternField } from './types'; /** * This is a special-case field which allows us to perform * document-level operations such as count. */ -export const documentField = { +export const documentField: IndexPatternField = { + displayName: i18n.translate('xpack.lens.indexPattern.records', { + defaultMessage: 'Records', + }), name: i18n.translate('xpack.lens.indexPattern.records', { defaultMessage: 'Records', }), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx index 08a2f85ec7053..781222888b6dc 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx @@ -36,30 +36,35 @@ describe('IndexPattern Field Item', () => { fields: [ { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, }, { name: 'bytes', + displayName: 'bytesLabel', type: 'number', aggregatable: true, searchable: true, }, { name: 'memory', + displayName: 'memory', type: 'number', aggregatable: true, searchable: true, }, { name: 'unsupported', + displayName: 'unsupported', type: 'geo', aggregatable: true, searchable: true, }, { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, @@ -83,6 +88,7 @@ describe('IndexPattern Field Item', () => { filters: [], field: { name: 'bytes', + displayName: 'bytesLabel', type: 'number', aggregatable: true, searchable: true, @@ -98,6 +104,13 @@ describe('IndexPattern Field Item', () => { } as unknown) as DataPublicPluginStart['fieldFormats']; }); + it('should display displayName of a field', () => { + const wrapper = mountWithIntl(); + expect(wrapper.find('[data-test-subj="lnsFieldListPanelField"]').first().text()).toEqual( + 'bytesLabel' + ); + }); + it('should request field stats without a time field, if the index pattern has none', async () => { indexPattern.timeFieldName = undefined; core.http.post.mockImplementationOnce(() => { @@ -149,6 +162,7 @@ describe('IndexPattern Field Item', () => { timeFieldName: 'timestamp', field: { name: 'bytes', + displayName: 'bytesLabel', type: 'number', aggregatable: true, searchable: true, @@ -235,6 +249,7 @@ describe('IndexPattern Field Item', () => { timeFieldName: 'timestamp', field: { name: 'bytes', + displayName: 'bytesLabel', type: 'number', aggregatable: true, searchable: true, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx index 5dc6673bc29ec..5bcfbc64ec706 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx @@ -100,7 +100,7 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { isLoading: false, }); - const wrappableName = wrapOnDot(field.name)!; + const wrappableName = wrapOnDot(field.displayName)!; const wrappableHighlight = wrapOnDot(highlight); const highlightIndex = wrappableHighlight ? wrappableName.toLowerCase().indexOf(wrappableHighlight.toLowerCase()) @@ -204,7 +204,7 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { container={document.querySelector('.application') || undefined} button={ = { id: 'indexpattern', @@ -129,6 +131,7 @@ export function getIndexPatternDatasource({ savedObjectsClient: await savedObjectsClient, defaultIndexPatternId: core.uiSettings.get('defaultIndex'), storage, + indexPatternsService, }); }, @@ -209,9 +212,9 @@ export function getIndexPatternDatasource({ id, state, setState, - savedObjectsClient, onError: onIndexPatternLoadError, storage, + indexPatternsService, }); }} data={data} @@ -289,7 +292,6 @@ export function getIndexPatternDatasource({ { changeLayerIndexPattern({ - savedObjectsClient, indexPatternId, setState: props.setState, state: props.state, @@ -297,6 +299,7 @@ export function getIndexPatternDatasource({ onError: onIndexPatternLoadError, replaceIfPossible: true, storage, + indexPatternsService, }); }} {...props} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx index b6246c6e91e7e..5489dcffc52c4 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx @@ -23,36 +23,42 @@ const expectedIndexPatterns = { fields: [ { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, }, { name: 'start_date', + displayName: 'start_date', type: 'date', aggregatable: true, searchable: true, }, { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, }, { name: 'memory', + displayName: 'memory', type: 'number', aggregatable: true, searchable: true, }, { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, }, { name: 'dest', + displayName: 'dest', type: 'string', aggregatable: true, searchable: true, @@ -66,6 +72,7 @@ const expectedIndexPatterns = { fields: [ { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, @@ -80,6 +87,7 @@ const expectedIndexPatterns = { }, { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, @@ -105,6 +113,7 @@ const expectedIndexPatterns = { }, { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, @@ -169,6 +178,7 @@ describe('IndexPattern Data Source suggestions', () => { it('should apply a bucketed aggregation for a string field', () => { const suggestions = getDatasourceSuggestionsForField(stateWithoutLayer(), '1', { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, @@ -214,6 +224,7 @@ describe('IndexPattern Data Source suggestions', () => { it('should apply a bucketed aggregation for a date field', () => { const suggestions = getDatasourceSuggestionsForField(stateWithoutLayer(), '1', { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, @@ -258,6 +269,7 @@ describe('IndexPattern Data Source suggestions', () => { it('should select a metric for a number field', () => { const suggestions = getDatasourceSuggestionsForField(stateWithoutLayer(), '1', { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, @@ -313,6 +325,7 @@ describe('IndexPattern Data Source suggestions', () => { fields: [ { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, @@ -331,6 +344,7 @@ describe('IndexPattern Data Source suggestions', () => { const suggestions = getDatasourceSuggestionsForField(state, '1', { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, @@ -374,6 +388,7 @@ describe('IndexPattern Data Source suggestions', () => { it('should apply a bucketed aggregation for a string field', () => { const suggestions = getDatasourceSuggestionsForField(stateWithEmptyLayer(), '1', { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, @@ -419,6 +434,7 @@ describe('IndexPattern Data Source suggestions', () => { it('should apply a bucketed aggregation for a date field', () => { const suggestions = getDatasourceSuggestionsForField(stateWithEmptyLayer(), '1', { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, @@ -463,6 +479,7 @@ describe('IndexPattern Data Source suggestions', () => { it('should select a metric for a number field', () => { const suggestions = getDatasourceSuggestionsForField(stateWithEmptyLayer(), '1', { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, @@ -518,6 +535,7 @@ describe('IndexPattern Data Source suggestions', () => { fields: [ { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, @@ -536,6 +554,7 @@ describe('IndexPattern Data Source suggestions', () => { const suggestions = getDatasourceSuggestionsForField(state, '1', { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, @@ -563,6 +582,7 @@ describe('IndexPattern Data Source suggestions', () => { it('creates a new layer and replaces layer if no match is found', () => { const suggestions = getDatasourceSuggestionsForField(stateWithEmptyLayer(), '2', { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, @@ -693,6 +713,7 @@ describe('IndexPattern Data Source suggestions', () => { '1', { name: 'start_date', + displayName: 'start_date', type: 'date', aggregatable: true, searchable: true, @@ -724,6 +745,7 @@ describe('IndexPattern Data Source suggestions', () => { const initialState = stateWithNonEmptyTables(); const suggestions = getDatasourceSuggestionsForField(initialState, '1', { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, @@ -770,6 +792,7 @@ describe('IndexPattern Data Source suggestions', () => { it('does not use the same field for bucketing multiple times', () => { const suggestions = getDatasourceSuggestionsForField(stateWithNonEmptyTables(), '1', { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, @@ -785,6 +808,7 @@ describe('IndexPattern Data Source suggestions', () => { const initialState = stateWithNonEmptyTables(); const suggestions = getDatasourceSuggestionsForField(initialState, '1', { name: 'dest', + displayName: 'dest', type: 'string', aggregatable: true, searchable: true, @@ -816,6 +840,7 @@ describe('IndexPattern Data Source suggestions', () => { const initialState = stateWithNonEmptyTables(); const suggestions = getDatasourceSuggestionsForField(initialState, '1', { name: 'memory', + displayName: 'memory', type: 'number', aggregatable: true, searchable: true, @@ -858,6 +883,7 @@ describe('IndexPattern Data Source suggestions', () => { }; const suggestions = getDatasourceSuggestionsForField(modifiedState, '1', { name: 'memory', + displayName: 'memory', type: 'number', aggregatable: true, searchable: true, @@ -908,6 +934,7 @@ describe('IndexPattern Data Source suggestions', () => { }; const suggestions = getDatasourceSuggestionsForField(modifiedState, '1', { name: 'memory', + displayName: 'memory', type: 'number', aggregatable: true, searchable: true, @@ -961,6 +988,7 @@ describe('IndexPattern Data Source suggestions', () => { const initialState = stateWithCurrentIndexPattern(); const suggestions = getDatasourceSuggestionsForField(initialState, '2', { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, @@ -1015,6 +1043,7 @@ describe('IndexPattern Data Source suggestions', () => { const initialState = stateWithCurrentIndexPattern(); const suggestions = getDatasourceSuggestionsForField(initialState, '1', { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, @@ -1185,7 +1214,7 @@ describe('IndexPattern Data Source suggestions', () => { { columnId: 'id1', operation: { - label: 'timestamp', + label: 'timestampLabel', dataType: 'date', isBucketed: true, scale: 'interval', @@ -1261,7 +1290,7 @@ describe('IndexPattern Data Source suggestions', () => { { columnId: 'id1', operation: { - label: 'timestamp', + label: 'timestampLabel', dataType: 'date', isBucketed: true, scale: 'interval', @@ -1324,30 +1353,35 @@ describe('IndexPattern Data Source suggestions', () => { fields: [ { name: 'field1', + displayName: 'field1', type: 'string', aggregatable: true, searchable: true, }, { name: 'field2', + displayName: 'field2', type: 'string', aggregatable: true, searchable: true, }, { name: 'field3', + displayName: 'field3Label', type: 'string', aggregatable: true, searchable: true, }, { name: 'field4', + displayName: 'field4', type: 'number', aggregatable: true, searchable: true, }, { name: 'field5', + displayName: 'field5', type: 'number', aggregatable: true, searchable: true, @@ -1462,12 +1496,14 @@ describe('IndexPattern Data Source suggestions', () => { fields: [ { name: 'field1', + displayName: 'field1', type: 'number', aggregatable: true, searchable: true, }, { name: 'field2', + displayName: 'field2', type: 'date', aggregatable: true, searchable: true, @@ -1522,6 +1558,7 @@ describe('IndexPattern Data Source suggestions', () => { fields: [ { name: 'field1', + displayName: 'field1', type: 'number', aggregatable: true, searchable: true, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts index 111a113a16be7..f3aa9c4f51c82 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts @@ -481,11 +481,19 @@ function createChangedNestingSuggestion(state: IndexPatternPrivateState, layerId const layer = state.layers[layerId]; const [firstBucket, secondBucket, ...rest] = layer.columnOrder; const updatedLayer = { ...layer, columnOrder: [secondBucket, firstBucket, ...rest] }; + const currentFields = state.indexPatterns[state.currentIndexPatternId].fields; + const firstBucketLabel = + currentFields.find((field) => field.name === layer.columns[firstBucket].sourceField) + ?.displayName || ''; + const secondBucketLabel = + currentFields.find((field) => field.name === layer.columns[secondBucket].sourceField) + ?.displayName || ''; + return buildSuggestion({ state, layerId, updatedLayer, - label: getNestedTitle([layer.columns[secondBucket], layer.columns[firstBucket]]), + label: getNestedTitle([secondBucketLabel, firstBucketLabel]), changeType: 'reorder', }); } @@ -544,12 +552,12 @@ function createMetricSuggestion( }); } -function getNestedTitle([outerBucket, innerBucket]: IndexPatternColumn[]) { +function getNestedTitle([outerBucketLabel, innerBucketLabel]: string[]) { return i18n.translate('xpack.lens.indexpattern.suggestions.nestingChangeLabel', { defaultMessage: '{innerOperation} for each {outerOperation}', values: { - innerOperation: innerBucket.sourceField, - outerOperation: outerBucket.sourceField, + innerOperation: innerBucketLabel, + outerOperation: outerBucketLabel, }, }); } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.test.tsx index 560c48b2155ee..738cdd611a7ba 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.test.tsx @@ -65,30 +65,35 @@ const initialState: IndexPatternPrivateState = { fields: [ { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, }, { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, }, { name: 'memory', + displayName: 'memory', type: 'number', aggregatable: true, searchable: true, }, { name: 'unsupported', + displayName: 'unsupported', type: 'geo', aggregatable: true, searchable: true, }, { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, @@ -102,6 +107,7 @@ const initialState: IndexPatternPrivateState = { fields: [ { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, @@ -116,6 +122,7 @@ const initialState: IndexPatternPrivateState = { }, { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, @@ -137,6 +144,7 @@ const initialState: IndexPatternPrivateState = { }, { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, @@ -155,18 +163,21 @@ const initialState: IndexPatternPrivateState = { fields: [ { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, }, { name: 'memory', + displayName: 'memory', type: 'number', aggregatable: true, searchable: true, }, { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts index 27904a0f23f16..cfabcb4edcef7 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts @@ -13,7 +13,14 @@ import { changeLayerIndexPattern, syncExistingFields, } from './loader'; -import { IndexPatternPersistedState, IndexPatternPrivateState, IndexPatternField } from './types'; +import { IndexPatternsContract } from '../../../../../src/plugins/data/public'; +import { + IndexPatternPersistedState, + IndexPatternPrivateState, + IndexPatternField, + IndexPattern, +} from './types'; +import { createMockedRestrictedIndexPattern, createMockedIndexPattern } from './mocks'; import { documentField } from './document_field'; jest.mock('./operations'); @@ -27,154 +34,157 @@ const createMockStorage = (lastData?: Record) => { }; }; -const sampleIndexPatterns = { - a: { - id: 'a', - title: 'my-fake-index-pattern', - timeFieldName: 'timestamp', - fields: [ - { - name: 'timestamp', - type: 'date', - aggregatable: true, - searchable: true, - }, - { - name: 'start_date', - type: 'date', - aggregatable: true, - searchable: true, - }, - { - name: 'bytes', - type: 'number', - aggregatable: true, - searchable: true, - }, - { - name: 'memory', - type: 'number', - aggregatable: true, - searchable: true, - }, - { - name: 'source', - type: 'string', - aggregatable: true, - searchable: true, - esTypes: ['keyword'], - }, - { - name: 'dest', - type: 'string', - aggregatable: true, - searchable: true, - esTypes: ['keyword'], - }, - documentField, - ], - }, - b: { - id: 'b', - title: 'my-fake-restricted-pattern', - timeFieldName: 'timestamp', - fields: [ - { - name: 'timestamp', - type: 'date', - aggregatable: true, - searchable: true, - aggregationRestrictions: { - date_histogram: { - agg: 'date_histogram', - fixed_interval: '1d', - delay: '7d', - time_zone: 'UTC', - }, +const indexPattern1 = ({ + id: '1', + title: 'my-fake-index-pattern', + timeFieldName: 'timestamp', + fields: [ + { + name: 'timestamp', + displayName: 'timestampLabel', + type: 'date', + aggregatable: true, + searchable: true, + }, + { + name: 'start_date', + displayName: 'start_date', + type: 'date', + aggregatable: true, + searchable: true, + }, + { + name: 'bytes', + displayName: 'bytes', + type: 'number', + aggregatable: true, + searchable: true, + }, + { + name: 'memory', + displayName: 'memory', + type: 'number', + aggregatable: true, + searchable: true, + }, + { + name: 'source', + displayName: 'source', + type: 'string', + aggregatable: true, + searchable: true, + esTypes: ['keyword'], + }, + { + name: 'unsupported', + displayName: 'unsupported', + type: 'geo', + aggregatable: true, + searchable: true, + }, + { + name: 'dest', + displayName: 'dest', + type: 'string', + aggregatable: true, + searchable: true, + esTypes: ['keyword'], + }, + documentField, + ], +} as unknown) as IndexPattern; + +const sampleIndexPatternsFromService = { + '1': createMockedIndexPattern(), + '2': createMockedRestrictedIndexPattern(), +}; + +const indexPattern2 = ({ + id: '2', + title: 'my-fake-restricted-pattern', + timeFieldName: 'timestamp', + fields: [ + { + name: 'timestamp', + displayName: 'timestampLabel', + type: 'date', + aggregatable: true, + searchable: true, + aggregationRestrictions: { + date_histogram: { + agg: 'date_histogram', + fixed_interval: '1d', + delay: '7d', + time_zone: 'UTC', }, }, - { - name: 'bytes', - type: 'number', - aggregatable: true, - searchable: true, - aggregationRestrictions: { - // Ignored in the UI - histogram: { - agg: 'histogram', - interval: 1000, - }, - avg: { - agg: 'avg', - }, - max: { - agg: 'max', - }, - min: { - agg: 'min', - }, - sum: { - agg: 'sum', - }, + }, + { + name: 'bytes', + displayName: 'bytes', + type: 'number', + aggregatable: true, + searchable: true, + aggregationRestrictions: { + // Ignored in the UI + histogram: { + agg: 'histogram', + interval: 1000, }, - }, - { - name: 'source', - type: 'string', - aggregatable: false, - searchable: false, - scripted: true, - aggregationRestrictions: { - terms: { - agg: 'terms', - }, + avg: { + agg: 'avg', + }, + max: { + agg: 'max', + }, + min: { + agg: 'min', + }, + sum: { + agg: 'sum', }, - esTypes: ['keyword'], }, - documentField, - ], - }, -}; - -function indexPatternSavedObject({ id }: { id: keyof typeof sampleIndexPatterns }) { - const pattern = { - ...sampleIndexPatterns[id], - fields: [ - ...sampleIndexPatterns[id].fields, - { - name: 'description', - type: 'string', - aggregatable: false, - searchable: true, - esTypes: ['text'], + }, + { + name: 'source', + displayName: 'source', + type: 'string', + aggregatable: true, + searchable: true, + scripted: true, + aggregationRestrictions: { + terms: { + agg: 'terms', + }, }, - ], - }; - return { - id, - type: 'index-pattern', - attributes: { - title: pattern.title, - timeFieldName: pattern.timeFieldName, - fields: JSON.stringify(pattern.fields.filter((f) => f.type !== 'document')), + esTypes: ['keyword'], }, - }; -} + documentField, + ], +} as unknown) as IndexPattern; + +const sampleIndexPatterns = { + '1': indexPattern1, + '2': indexPattern2, +}; function mockClient() { return ({ find: jest.fn(async () => ({ savedObjects: [ - { id: 'a', attributes: { title: sampleIndexPatterns.a.title } }, - { id: 'b', attributes: { title: sampleIndexPatterns.b.title } }, + { id: '1', attributes: { title: sampleIndexPatterns[1].title } }, + { id: '2', attributes: { title: sampleIndexPatterns[2].title } }, ], })), - async bulkGet(indexPatterns: Array<{ id: keyof typeof sampleIndexPatterns }>) { - return { - savedObjects: indexPatterns.map(({ id }) => indexPatternSavedObject({ id })), - }; - }, - } as unknown) as Pick; + } as unknown) as Pick; +} + +function mockIndexPatternsService() { + return ({ + get: jest.fn(async (id: '1' | '2') => { + return sampleIndexPatternsFromService[id]; + }), + } as unknown) as Pick; } describe('loader', () => { @@ -182,11 +192,12 @@ describe('loader', () => { it('should not load index patterns that are already loaded', async () => { const cache = await loadIndexPatterns({ cache: sampleIndexPatterns, - patterns: ['a', 'b'], - savedObjectsClient: { - bulkGet: jest.fn(() => Promise.reject('bulkGet should not have been called')), - find: jest.fn(() => Promise.reject('find should not have been called')), - }, + patterns: ['1', '2'], + indexPatternsService: ({ + get: jest.fn(() => + Promise.reject('mockIndexPatternService.get should not have been called') + ), + } as unknown) as Pick, }); expect(cache).toEqual(sampleIndexPatterns); @@ -195,10 +206,10 @@ describe('loader', () => { it('should load index patterns that are not loaded', async () => { const cache = await loadIndexPatterns({ cache: { - b: sampleIndexPatterns.b, + '2': sampleIndexPatterns['2'], }, - patterns: ['a', 'b'], - savedObjectsClient: mockClient(), + patterns: ['1', '2'], + indexPatternsService: mockIndexPatternsService(), }); expect(cache).toMatchObject(sampleIndexPatterns); @@ -207,8 +218,8 @@ describe('loader', () => { it('should allow scripted, but not full text fields', async () => { const cache = await loadIndexPatterns({ cache: {}, - patterns: ['a', 'b'], - savedObjectsClient: mockClient(), + patterns: ['1', '2'], + indexPatternsService: mockIndexPatternsService(), }); expect(cache).toMatchObject(sampleIndexPatterns); @@ -218,61 +229,56 @@ describe('loader', () => { const cache = await loadIndexPatterns({ cache: {}, patterns: ['foo'], - savedObjectsClient: ({ - ...mockClient(), - async bulkGet() { - return { - savedObjects: [ - { - id: 'foo', - type: 'index-pattern', - attributes: { - title: 'Foo index', - typeMeta: JSON.stringify({ - aggs: { - date_histogram: { - timestamp: { - agg: 'date_histogram', - fixed_interval: 'm', - }, - }, - sum: { - bytes: { - agg: 'sum', - }, - }, - }, - }), - fields: JSON.stringify([ - { - name: 'timestamp', - type: 'date', - aggregatable: true, - searchable: true, - }, - { - name: 'bytes', - type: 'number', - aggregatable: true, - searchable: true, - }, - ]), + indexPatternsService: ({ + get: jest.fn(async () => ({ + id: 'foo', + title: 'Foo index', + typeMeta: { + aggs: { + date_histogram: { + timestamp: { + agg: 'date_histogram', + fixed_interval: 'm', }, }, - ], - }; - }, - } as unknown) as Pick, + sum: { + bytes: { + agg: 'sum', + }, + }, + }, + }, + fields: [ + { + name: 'timestamp', + displayName: 'timestampLabel', + type: 'date', + aggregatable: true, + searchable: true, + }, + { + name: 'bytes', + displayName: 'bytes', + type: 'number', + aggregatable: true, + searchable: true, + }, + ], + })), + } as unknown) as Pick, }); - expect(cache.foo.fields.find((f) => f.name === 'bytes')!.aggregationRestrictions).toEqual({ + expect( + cache.foo.fields.find((f: IndexPatternField) => f.name === 'bytes')!.aggregationRestrictions + ).toEqual({ sum: { agg: 'sum' }, }); - expect(cache.foo.fields.find((f) => f.name === 'timestamp')!.aggregationRestrictions).toEqual( - { - date_histogram: { agg: 'date_histogram', fixed_interval: 'm' }, - } - ); + expect( + cache.foo.fields.find((f: IndexPatternField) => f.name === 'timestamp')! + .aggregationRestrictions + ).toEqual({ + date_histogram: { agg: 'date_histogram', fixed_interval: 'm' }, + }); }); }); @@ -281,22 +287,23 @@ describe('loader', () => { const storage = createMockStorage(); const state = await loadInitialState({ savedObjectsClient: mockClient(), + indexPatternsService: mockIndexPatternsService(), storage, }); expect(state).toMatchObject({ - currentIndexPatternId: 'a', + currentIndexPatternId: '1', indexPatternRefs: [ - { id: 'a', title: sampleIndexPatterns.a.title }, - { id: 'b', title: sampleIndexPatterns.b.title }, + { id: '1', title: sampleIndexPatterns['1'].title }, + { id: '2', title: sampleIndexPatterns['2'].title }, ], indexPatterns: { - a: sampleIndexPatterns.a, + '1': sampleIndexPatterns['1'], }, layers: {}, }); expect(storage.set).toHaveBeenCalledWith('lens-settings', { - indexPatternId: 'a', + indexPatternId: '1', }); }); @@ -304,39 +311,41 @@ describe('loader', () => { const storage = createMockStorage({ indexPatternId: 'c' }); const state = await loadInitialState({ savedObjectsClient: mockClient(), + indexPatternsService: mockIndexPatternsService(), storage, }); expect(state).toMatchObject({ - currentIndexPatternId: 'a', + currentIndexPatternId: '1', indexPatternRefs: [ - { id: 'a', title: sampleIndexPatterns.a.title }, - { id: 'b', title: sampleIndexPatterns.b.title }, + { id: '1', title: sampleIndexPatterns['1'].title }, + { id: '2', title: sampleIndexPatterns['2'].title }, ], indexPatterns: { - a: sampleIndexPatterns.a, + '1': sampleIndexPatterns['1'], }, layers: {}, }); expect(storage.set).toHaveBeenCalledWith('lens-settings', { - indexPatternId: 'a', + indexPatternId: '1', }); }); it('should load lastUsedIndexPatternId if in localStorage', async () => { const state = await loadInitialState({ savedObjectsClient: mockClient(), - storage: createMockStorage({ indexPatternId: 'b' }), + indexPatternsService: mockIndexPatternsService(), + storage: createMockStorage({ indexPatternId: '2' }), }); expect(state).toMatchObject({ - currentIndexPatternId: 'b', + currentIndexPatternId: '2', indexPatternRefs: [ - { id: 'a', title: sampleIndexPatterns.a.title }, - { id: 'b', title: sampleIndexPatterns.b.title }, + { id: '1', title: sampleIndexPatterns['1'].title }, + { id: '2', title: sampleIndexPatterns['2'].title }, ], indexPatterns: { - b: sampleIndexPatterns.b, + '2': sampleIndexPatterns['2'], }, layers: {}, }); @@ -345,33 +354,34 @@ describe('loader', () => { it('should use the default index pattern id, if provided', async () => { const storage = createMockStorage(); const state = await loadInitialState({ - defaultIndexPatternId: 'b', + defaultIndexPatternId: '2', savedObjectsClient: mockClient(), + indexPatternsService: mockIndexPatternsService(), storage, }); expect(state).toMatchObject({ - currentIndexPatternId: 'b', + currentIndexPatternId: '2', indexPatternRefs: [ - { id: 'a', title: sampleIndexPatterns.a.title }, - { id: 'b', title: sampleIndexPatterns.b.title }, + { id: '1', title: sampleIndexPatterns['1'].title }, + { id: '2', title: sampleIndexPatterns['2'].title }, ], indexPatterns: { - b: sampleIndexPatterns.b, + '2': sampleIndexPatterns['2'], }, layers: {}, }); expect(storage.set).toHaveBeenCalledWith('lens-settings', { - indexPatternId: 'b', + indexPatternId: '2', }); }); it('should initialize from saved state', async () => { const savedState: IndexPatternPersistedState = { - currentIndexPatternId: 'b', + currentIndexPatternId: '2', layers: { layerb: { - indexPatternId: 'b', + indexPatternId: '2', columnOrder: ['col1', 'col2'], columns: { col1: { @@ -395,27 +405,28 @@ describe('loader', () => { }, }, }; - const storage = createMockStorage({ indexPatternId: 'a' }); + const storage = createMockStorage({ indexPatternId: '1' }); const state = await loadInitialState({ state: savedState, savedObjectsClient: mockClient(), + indexPatternsService: mockIndexPatternsService(), storage, }); expect(state).toMatchObject({ - currentIndexPatternId: 'b', + currentIndexPatternId: '2', indexPatternRefs: [ - { id: 'a', title: sampleIndexPatterns.a.title }, - { id: 'b', title: sampleIndexPatterns.b.title }, + { id: '1', title: sampleIndexPatterns['1'].title }, + { id: '2', title: sampleIndexPatterns['2'].title }, ], indexPatterns: { - b: sampleIndexPatterns.b, + '2': sampleIndexPatterns['2'], }, layers: savedState.layers, }); expect(storage.set).toHaveBeenCalledWith('lens-settings', { - indexPatternId: 'b', + indexPatternId: '2', }); }); }); @@ -424,33 +435,36 @@ describe('loader', () => { it('loads the index pattern and then sets it as current', async () => { const setState = jest.fn(); const state: IndexPatternPrivateState = { - currentIndexPatternId: 'b', + currentIndexPatternId: '2', indexPatternRefs: [], indexPatterns: {}, existingFields: {}, layers: {}, isFirstExistenceFetch: false, }; - const storage = createMockStorage({ indexPatternId: 'b' }); + const storage = createMockStorage({ indexPatternId: '2' }); await changeIndexPattern({ state, setState, - id: 'a', - savedObjectsClient: mockClient(), + id: '1', + indexPatternsService: mockIndexPatternsService(), onError: jest.fn(), storage, }); expect(setState).toHaveBeenCalledTimes(1); expect(setState.mock.calls[0][0](state)).toMatchObject({ - currentIndexPatternId: 'a', + currentIndexPatternId: '1', indexPatterns: { - a: sampleIndexPatterns.a, + '1': { + ...sampleIndexPatterns['1'], + fields: [...sampleIndexPatterns['1'].fields], + }, }, }); expect(storage.set).toHaveBeenCalledWith('lens-settings', { - indexPatternId: 'a', + indexPatternId: '1', }); }); @@ -459,7 +473,7 @@ describe('loader', () => { const onError = jest.fn(); const err = new Error('NOPE!'); const state: IndexPatternPrivateState = { - currentIndexPatternId: 'b', + currentIndexPatternId: '2', indexPatternRefs: [], existingFields: {}, indexPatterns: {}, @@ -467,15 +481,14 @@ describe('loader', () => { isFirstExistenceFetch: false, }; - const storage = createMockStorage({ indexPatternId: 'b' }); + const storage = createMockStorage({ indexPatternId: '2' }); await changeIndexPattern({ state, setState, - id: 'a', - savedObjectsClient: { - ...mockClient(), - bulkGet: jest.fn(async () => { + id: '1', + indexPatternsService: { + get: jest.fn(async () => { throw err; }), }, @@ -493,17 +506,17 @@ describe('loader', () => { it('loads the index pattern and then changes the specified layer', async () => { const setState = jest.fn(); const state: IndexPatternPrivateState = { - currentIndexPatternId: 'b', + currentIndexPatternId: '2', indexPatternRefs: [], existingFields: {}, indexPatterns: { - a: sampleIndexPatterns.a, + '1': sampleIndexPatterns['1'], }, layers: { l0: { columnOrder: ['col1'], columns: {}, - indexPatternId: 'a', + indexPatternId: '1', }, l1: { columnOrder: ['col2'], @@ -519,36 +532,36 @@ describe('loader', () => { sourceField: 'timestamp', }, }, - indexPatternId: 'a', + indexPatternId: '1', }, }, isFirstExistenceFetch: false, }; - const storage = createMockStorage({ indexPatternId: 'a' }); + const storage = createMockStorage({ indexPatternId: '1' }); await changeLayerIndexPattern({ state, setState, - indexPatternId: 'b', + indexPatternId: '2', layerId: 'l1', - savedObjectsClient: mockClient(), + indexPatternsService: mockIndexPatternsService(), onError: jest.fn(), storage, }); expect(setState).toHaveBeenCalledTimes(1); expect(setState.mock.calls[0][0](state)).toMatchObject({ - currentIndexPatternId: 'b', + currentIndexPatternId: '2', indexPatterns: { - a: sampleIndexPatterns.a, - b: sampleIndexPatterns.b, + 1: sampleIndexPatterns['1'], + 2: sampleIndexPatterns['2'], }, layers: { l0: { columnOrder: ['col1'], columns: {}, - indexPatternId: 'a', + indexPatternId: '1', }, l1: { columnOrder: ['col2'], @@ -564,12 +577,12 @@ describe('loader', () => { sourceField: 'timestamp', }, }, - indexPatternId: 'b', + indexPatternId: '2', }, }, }); expect(storage.set).toHaveBeenCalledWith('lens-settings', { - indexPatternId: 'b', + indexPatternId: '2', }); }); @@ -578,32 +591,31 @@ describe('loader', () => { const onError = jest.fn(); const err = new Error('NOPE!'); const state: IndexPatternPrivateState = { - currentIndexPatternId: 'b', + currentIndexPatternId: '2', indexPatternRefs: [], existingFields: {}, indexPatterns: { - a: sampleIndexPatterns.a, + '1': sampleIndexPatterns['1'], }, layers: { l0: { columnOrder: ['col1'], columns: {}, - indexPatternId: 'a', + indexPatternId: '1', }, }, isFirstExistenceFetch: false, }; - const storage = createMockStorage({ indexPatternId: 'b' }); + const storage = createMockStorage({ indexPatternId: '2' }); await changeLayerIndexPattern({ state, setState, - indexPatternId: 'b', + indexPatternId: '2', layerId: 'l0', - savedObjectsClient: { - ...mockClient(), - bulkGet: jest.fn(async () => { + indexPatternsService: { + get: jest.fn(async () => { throw err; }), }, @@ -634,7 +646,7 @@ describe('loader', () => { return { indexPatternTitle, existingFieldNames: ['field_1', 'field_2'].map( - (fieldName) => `${indexPatternTitle}_${fieldName}` + (fieldName) => `ip${indexPatternTitle}_${fieldName}` ), }; }) as unknown) as HttpHandler; @@ -643,9 +655,9 @@ describe('loader', () => { dateRange: { fromDate: '1900-01-01', toDate: '2000-01-01' }, fetchJson, indexPatterns: [ - { id: 'a', title: 'a', fields: [] }, - { id: 'b', title: 'a', fields: [] }, - { id: 'c', title: 'a', fields: [] }, + { id: '1', title: '1', fields: [] }, + { id: '2', title: '1', fields: [] }, + { id: '3', title: '1', fields: [] }, ], setState, dslQuery, @@ -668,9 +680,9 @@ describe('loader', () => { isFirstExistenceFetch: false, existenceFetchFailed: false, existingFields: { - a: { a_field_1: true, a_field_2: true }, - b: { b_field_1: true, b_field_2: true }, - c: { c_field_1: true, c_field_2: true }, + '1': { ip1_field_1: true, ip1_field_2: true }, + '2': { ip2_field_1: true, ip2_field_2: true }, + '3': { ip3_field_1: true, ip3_field_2: true }, }, }); }); @@ -683,7 +695,7 @@ describe('loader', () => { return { indexPatternTitle, existingFieldNames: - indexPatternTitle === 'a' + indexPatternTitle === '1' ? ['field_1', 'field_2'].map((fieldName) => `${indexPatternTitle}_${fieldName}`) : [], }; @@ -693,9 +705,9 @@ describe('loader', () => { dateRange: { fromDate: '1900-01-01', toDate: '2000-01-01' }, fetchJson, indexPatterns: [ - { id: 'a', title: 'a', fields: [] }, - { id: 'b', title: 'a', fields: [] }, - { id: 'c', title: 'a', fields: [] }, + { id: '1', title: '1', fields: [] }, + { id: '2', title: '1', fields: [] }, + { id: 'c', title: '1', fields: [] }, ], setState, dslQuery, @@ -725,8 +737,8 @@ describe('loader', () => { fetchJson, indexPatterns: [ { - id: 'a', - title: 'a', + id: '1', + title: '1', fields: [{ name: 'field1' }, { name: 'field2' }] as IndexPatternField[], }, ], @@ -746,7 +758,7 @@ describe('loader', () => { }) as IndexPatternPrivateState; expect(newState.existenceFetchFailed).toEqual(true); - expect(newState.existingFields.a).toEqual({ + expect(newState.existingFields['1']).toEqual({ field1: true, field2: true, }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index 20e7bec6db131..9c4a19e58a052 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -6,8 +6,7 @@ import _ from 'lodash'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; -import { SavedObjectsClientContract, SavedObjectAttributes, HttpSetup } from 'kibana/public'; -import { SimpleSavedObject } from 'kibana/public'; +import { SavedObjectsClientContract, HttpSetup } from 'kibana/public'; import { StateSetter } from '../types'; import { IndexPattern, @@ -19,33 +18,25 @@ import { import { updateLayerIndexPattern } from './state_helpers'; import { DateRange, ExistingFields } from '../../common/types'; import { BASE_API_URL } from '../../common'; -import { documentField } from './document_field'; import { + IndexPatternsContract, indexPatterns as indexPatternsUtils, - IFieldType, - IndexPatternTypeMeta, } from '../../../../../src/plugins/data/public'; +import { documentField } from './document_field'; import { readFromStorage, writeToStorage } from '../settings_storage'; -interface SavedIndexPatternAttributes extends SavedObjectAttributes { - title: string; - timeFieldName: string | null; - fields: string; - fieldFormatMap: string; - typeMeta: string; -} - type SetState = StateSetter; -type SavedObjectsClient = Pick; +type SavedObjectsClient = Pick; +type IndexPatternsService = Pick; type ErrorHandler = (err: Error) => void; export async function loadIndexPatterns({ + indexPatternsService, patterns, - savedObjectsClient, cache, }: { + indexPatternsService: IndexPatternsService; patterns: string[]; - savedObjectsClient: SavedObjectsClient; cache: Record; }) { const missingIds = patterns.filter((id) => !cache[id]); @@ -54,20 +45,62 @@ export async function loadIndexPatterns({ return cache; } - const resp = await savedObjectsClient.bulkGet( - missingIds.map((id) => ({ id, type: 'index-pattern' })) - ); + const indexPatterns = await Promise.all(missingIds.map((id) => indexPatternsService.get(id))); + const indexPatternsObject = indexPatterns.reduce( + (acc, indexPattern) => { + const newFields = indexPattern.fields + .filter( + (field) => + !indexPatternsUtils.isNestedField(field) && (!!field.aggregatable || !!field.scripted) + ) + .map( + (field): IndexPatternField => ({ + name: field.name, + displayName: field.displayName, + type: field.type, + aggregatable: field.aggregatable, + searchable: field.searchable, + scripted: field.scripted, + esTypes: field.esTypes, + }) + ) + .concat(documentField); + + const { typeMeta, title, timeFieldName, fieldFormatMap } = indexPattern; + if (typeMeta?.aggs) { + const aggs = Object.keys(typeMeta.aggs); + newFields.forEach((field, index) => { + const restrictionsObj: IndexPatternField['aggregationRestrictions'] = {}; + aggs.forEach((agg) => { + const restriction = + typeMeta.aggs && typeMeta.aggs[agg] && typeMeta.aggs[agg][field.name]; + if (restriction) { + restrictionsObj[agg] = restriction; + } + }); + if (Object.keys(restrictionsObj).length) { + newFields[index] = { ...field, aggregationRestrictions: restrictionsObj }; + } + }); + } - return resp.savedObjects.reduce( - (acc, savedObject) => { - const indexPattern = fromSavedObject( - savedObject as SimpleSavedObject - ); - acc[indexPattern.id] = indexPattern; - return acc; + const currentIndexPattern: IndexPattern = { + id: indexPattern.id!, // id exists for sure because we got index patterns by id + title, + timeFieldName, + fieldFormatMap, + fields: newFields, + }; + + return { + [currentIndexPattern.id]: currentIndexPattern, + ...acc, + }; }, { ...cache } ); + + return indexPatternsObject; } const getLastUsedIndexPatternId = ( @@ -87,11 +120,13 @@ export async function loadInitialState({ savedObjectsClient, defaultIndexPatternId, storage, + indexPatternsService, }: { state?: IndexPatternPersistedState; savedObjectsClient: SavedObjectsClient; defaultIndexPatternId?: string; storage: IStorageWrapper; + indexPatternsService: IndexPatternsService; }): Promise { const indexPatternRefs = await loadIndexPatternRefs(savedObjectsClient); const lastUsedIndexPatternId = getLastUsedIndexPatternId(storage, indexPatternRefs); @@ -108,7 +143,7 @@ export async function loadInitialState({ setLastUsedIndexPatternId(storage, currentIndexPatternId); const indexPatterns = await loadIndexPatterns({ - savedObjectsClient, + indexPatternsService, cache: {}, patterns: requiredPatterns, }); @@ -135,22 +170,22 @@ export async function loadInitialState({ export async function changeIndexPattern({ id, - savedObjectsClient, state, setState, onError, storage, + indexPatternsService, }: { id: string; - savedObjectsClient: SavedObjectsClient; state: IndexPatternPrivateState; setState: SetState; onError: ErrorHandler; storage: IStorageWrapper; + indexPatternsService: IndexPatternsService; }) { try { const indexPatterns = await loadIndexPatterns({ - savedObjectsClient, + indexPatternsService, cache: state.indexPatterns, patterns: [id], }); @@ -175,25 +210,25 @@ export async function changeIndexPattern({ export async function changeLayerIndexPattern({ indexPatternId, layerId, - savedObjectsClient, state, setState, onError, replaceIfPossible, storage, + indexPatternsService, }: { indexPatternId: string; layerId: string; - savedObjectsClient: SavedObjectsClient; state: IndexPatternPrivateState; setState: SetState; onError: ErrorHandler; replaceIfPossible?: boolean; storage: IStorageWrapper; + indexPatternsService: IndexPatternsService; }) { try { const indexPatterns = await loadIndexPatterns({ - savedObjectsClient, + indexPatternsService, cache: state.indexPatterns, patterns: [indexPatternId], }); @@ -319,55 +354,3 @@ function isSingleEmptyLayer(layerMap: IndexPatternPrivateState['layers']) { const layers = Object.values(layerMap); return layers.length === 1 && layers[0].columnOrder.length === 0; } - -function fromSavedObject( - savedObject: SimpleSavedObject -): IndexPattern { - const { id, attributes, type } = savedObject; - const indexPattern = { - ...attributes, - id, - type, - title: attributes.title, - fields: (JSON.parse(attributes.fields) as IFieldType[]) - .filter( - (field) => - !indexPatternsUtils.isNestedField(field) && (!!field.aggregatable || !!field.scripted) - ) - .concat(documentField) as IndexPatternField[], - typeMeta: attributes.typeMeta - ? (JSON.parse(attributes.typeMeta) as IndexPatternTypeMeta) - : undefined, - fieldFormatMap: attributes.fieldFormatMap ? JSON.parse(attributes.fieldFormatMap) : undefined, - }; - - const { typeMeta } = indexPattern; - if (!typeMeta) { - return indexPattern; - } - - const newFields = [...(indexPattern.fields as IndexPatternField[])]; - - if (typeMeta.aggs) { - const aggs = Object.keys(typeMeta.aggs); - newFields.forEach((field, index) => { - const restrictionsObj: IndexPatternField['aggregationRestrictions'] = {}; - aggs.forEach((agg) => { - const restriction = typeMeta.aggs && typeMeta.aggs[agg] && typeMeta.aggs[agg][field.name]; - if (restriction) { - restrictionsObj[agg] = restriction; - } - }); - if (Object.keys(restrictionsObj).length) { - newFields[index] = { ...field, aggregationRestrictions: restrictionsObj }; - } - }); - } - - return { - id: indexPattern.id, - title: indexPattern.title, - timeFieldName: indexPattern.timeFieldName || undefined, - fields: newFields, - }; -} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts b/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts index dff3e61342a6a..869eee67d381d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts @@ -14,39 +14,54 @@ export const createMockedIndexPattern = (): IndexPattern => ({ fields: [ { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, }, { name: 'start_date', + displayName: 'start_date', type: 'date', aggregatable: true, searchable: true, }, { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, }, { name: 'memory', + displayName: 'memory', type: 'number', aggregatable: true, searchable: true, }, { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, + esTypes: ['keyword'], + }, + { + name: 'unsupported', + displayName: 'unsupported', + type: 'geo', + aggregatable: true, + searchable: true, }, { name: 'dest', + displayName: 'dest', type: 'string', aggregatable: true, searchable: true, + esTypes: ['keyword'], }, ], }); @@ -58,21 +73,26 @@ export const createMockedRestrictedIndexPattern = () => ({ fields: [ { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', aggregatable: true, searchable: true, }, { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, }, { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, + scripted: true, + esTypes: ['keyword'], }, ], typeMeta: { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx index 09faa4bb70447..ad04891b637d4 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx @@ -51,7 +51,7 @@ export const cardinalityOperation: OperationDefinition { return { ...oldColumn, - label: ofName(field.name), + label: ofName(field.displayName), sourceField: field.name, }; }, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx index 1dcaf78b58a6c..4e081da2c6dc9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx @@ -26,7 +26,7 @@ export const countOperation: OperationDefinition = { onFieldChange: (oldColumn, indexPattern, field) => { return { ...oldColumn, - label: field.name, + label: field.displayName, sourceField: field.name, }; }, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx index ebf8e09e86396..48a6079c58ac0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx @@ -58,6 +58,7 @@ describe('date_histogram', () => { fields: [ { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', esTypes: ['date'], aggregatable: true, @@ -71,6 +72,7 @@ describe('date_histogram', () => { fields: [ { name: 'other_timestamp', + displayName: 'other_timestamp', type: 'date', esTypes: ['date'], aggregatable: true, @@ -168,6 +170,7 @@ describe('date_histogram', () => { indexPattern: createMockedIndexPattern(), field: { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', esTypes: ['date'], aggregatable: true, @@ -185,6 +188,7 @@ describe('date_histogram', () => { indexPattern: createMockedIndexPattern(), field: { name: 'start_date', + displayName: 'start_date', type: 'date', esTypes: ['date'], aggregatable: true, @@ -202,6 +206,7 @@ describe('date_histogram', () => { indexPattern: createMockedIndexPattern(), field: { name: 'timestamp', + displayName: 'timestampLabel', type: 'date', esTypes: ['date'], aggregatable: true, @@ -298,6 +303,7 @@ describe('date_histogram', () => { fields: [ { name: 'dateField', + displayName: 'dateField', type: 'date', aggregatable: true, searchable: true, @@ -340,6 +346,7 @@ describe('date_histogram', () => { fields: [ { name: 'dateField', + displayName: 'dateField', type: 'date', aggregatable: true, searchable: true, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx index 6e007c12acf42..2236bc576e2b6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx @@ -64,7 +64,7 @@ export const dateHistogramOperation: OperationDefinition { return { ...oldColumn, - label: field.name, + label: field.displayName, sourceField: field.name, }; }, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx index 3ede847a5e257..e6c8a5f6ac852 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx @@ -51,7 +51,7 @@ function buildMetricOperation>({ ); }, buildColumn: ({ suggestedPriority, field, previousColumn }) => ({ - label: ofName(field.name), + label: ofName(field.displayName), dataType: 'number', operationType: type, suggestedPriority, @@ -64,7 +64,7 @@ function buildMetricOperation>({ onFieldChange: (oldColumn, indexPattern, field) => { return { ...oldColumn, - label: ofName(field.name), + label: ofName(field.displayName), sourceField: field.name, }; }, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.test.tsx index d7f00e185a5bb..05bb2ef673888 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.test.tsx @@ -118,6 +118,7 @@ describe('terms', () => { aggregatable: true, searchable: true, name: 'test', + displayName: 'test', type: 'string', aggregationRestrictions: { terms: { @@ -136,6 +137,7 @@ describe('terms', () => { aggregatable: true, searchable: true, name: 'test', + displayName: 'test', type: 'number', aggregationRestrictions: { terms: { @@ -154,6 +156,7 @@ describe('terms', () => { aggregatable: true, searchable: true, name: 'test', + displayName: 'test', type: 'boolean', }) ).toEqual({ @@ -167,6 +170,7 @@ describe('terms', () => { aggregatable: true, searchable: true, name: 'test', + displayName: 'test', type: 'ip', }) ).toEqual({ @@ -182,6 +186,7 @@ describe('terms', () => { aggregatable: false, searchable: true, name: 'test', + displayName: 'test', type: 'string', }) ).toEqual(undefined); @@ -192,6 +197,7 @@ describe('terms', () => { aggregationRestrictions: {}, searchable: true, name: 'test', + displayName: 'test', type: 'string', }) ).toEqual(undefined); @@ -209,6 +215,7 @@ describe('terms', () => { searchable: true, type: 'boolean', name: 'test', + displayName: 'test', }, columns: {}, }); @@ -234,6 +241,7 @@ describe('terms', () => { searchable: true, type: 'boolean', name: 'test', + displayName: 'test', }, }); expect(termsColumn.params).toEqual( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx index 1ab58cb11c598..ac1ff9da2fea0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx @@ -79,7 +79,7 @@ export const termsOperation: OperationDefinition = { .map(([id]) => id)[0]; return { - label: ofName(field.name), + label: ofName(field.displayName), dataType: field.type as DataType, operationType: 'terms', scale: 'ordinal', @@ -115,7 +115,7 @@ export const termsOperation: OperationDefinition = { onFieldChange: (oldColumn, indexPattern, field) => { return { ...oldColumn, - label: ofName(field.name), + label: ofName(field.displayName), sourceField: field.name, }; }, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts index 1a37e5e4cf6a4..3fce2562f528e 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts @@ -19,18 +19,21 @@ const expectedIndexPatterns = { fields: [ { name: 'timestamp', + displayName: 'timestamp', type: 'date', aggregatable: true, searchable: true, }, { name: 'bytes', + displayName: 'bytes', type: 'number', aggregatable: true, searchable: true, }, { name: 'source', + displayName: 'source', type: 'string', aggregatable: true, searchable: true, @@ -46,6 +49,7 @@ describe('getOperationTypesForField', () => { getOperationTypesForField({ type: 'string', name: 'a', + displayName: 'aLabel', aggregatable: true, searchable: true, }) @@ -57,6 +61,7 @@ describe('getOperationTypesForField', () => { getOperationTypesForField({ type: 'number', name: 'a', + displayName: 'aLabel', aggregatable: true, searchable: true, }) @@ -68,6 +73,7 @@ describe('getOperationTypesForField', () => { getOperationTypesForField({ type: 'date', name: 'a', + displayName: 'aLabel', aggregatable: true, searchable: true, }) @@ -79,6 +85,7 @@ describe('getOperationTypesForField', () => { getOperationTypesForField({ type: '_source', name: 'a', + displayName: 'aLabel', aggregatable: true, searchable: true, }) @@ -92,6 +99,7 @@ describe('getOperationTypesForField', () => { getOperationTypesForField({ type: 'string', name: 'a', + displayName: 'aLabel', aggregatable: true, searchable: true, aggregationRestrictions: { @@ -108,6 +116,7 @@ describe('getOperationTypesForField', () => { getOperationTypesForField({ type: 'number', name: 'a', + displayName: 'aLabel', aggregatable: true, searchable: true, aggregationRestrictions: { @@ -127,6 +136,7 @@ describe('getOperationTypesForField', () => { getOperationTypesForField({ type: 'date', name: 'a', + displayName: 'aLabel', aggregatable: true, searchable: true, aggregationRestrictions: { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/state_helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/state_helpers.test.ts index d778749ef3940..d7fd0d3661c86 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/state_helpers.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/state_helpers.test.ts @@ -573,12 +573,14 @@ describe('state_helpers', () => { fields: [ { name: 'fieldA', + displayName: 'fieldA', aggregatable: true, searchable: true, type: 'string', }, { name: 'fieldB', + displayName: 'fieldB', aggregatable: true, searchable: true, type: 'number', @@ -590,12 +592,14 @@ describe('state_helpers', () => { }, { name: 'fieldC', + displayName: 'fieldC', aggregatable: false, searchable: true, type: 'date', }, { name: 'fieldD', + displayName: 'fieldD', aggregatable: true, searchable: true, type: 'date', @@ -609,6 +613,7 @@ describe('state_helpers', () => { }, { name: 'fieldE', + displayName: 'fieldE', aggregatable: true, searchable: true, type: 'date', diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/types.ts b/x-pack/plugins/lens/public/indexpattern_datasource/types.ts index 2a9b3f452d991..8d0e82b176aa9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/types.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/types.ts @@ -23,6 +23,7 @@ export interface IndexPattern { export interface IndexPatternField { name: string; + displayName: string; type: string; esTypes?: string[]; aggregatable: boolean;