From df8c41a5ba319bdb8b3b4d34d7efca10b98746ff Mon Sep 17 00:00:00 2001 From: Tim Roes Date: Thu, 14 Oct 2021 17:20:08 +0200 Subject: [PATCH] Deprecate DataView.flattenHit in favor of data plugin flattenHit (#114517) (#114991) * WIP replacing indexPattern.flattenHit by tabify * Fix jest tests * Read metaFields from index pattern * Remove old test code * remove unnecessary changes * Remove flattenHitWrapper APIs * Fix imports * Fix missing metaFields * Add all meta fields to allowlist * Improve inline comments * Move flattenHit test to new implementation * Add deprecation comment to implementation Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> # Conflicts: # src/plugins/data_views/public/index.ts --- .../__snapshots__/tabify_docs.test.ts.snap | 160 +---------------- .../data/common/search/tabify/index.ts | 2 +- .../common/search/tabify/tabify_docs.test.ts | 170 ++++++++++++------ .../data/common/search/tabify/tabify_docs.ts | 85 ++++++++- src/plugins/data/public/index.ts | 2 - .../data_views/common/data_views/data_view.ts | 3 + .../common/data_views/flatten_hit.ts | 17 +- src/plugins/data_views/public/index.ts | 1 - .../public/__mocks__/index_pattern.ts | 9 +- .../__mocks__/index_pattern_with_timefield.ts | 9 +- .../doc_table/components/table_row.tsx | 3 +- .../sidebar/discover_sidebar.test.tsx | 4 +- .../discover_sidebar_responsive.test.tsx | 6 +- .../sidebar/lib/field_calculator.js | 4 +- .../sidebar/lib/field_calculator.test.ts | 3 +- .../apps/main/utils/calc_field_counts.ts | 4 +- .../discover_grid/discover_grid.tsx | 4 +- .../discover_grid_cell_actions.tsx | 6 +- .../discover_grid_document_selection.test.tsx | 45 ++--- .../discover_grid_expand_button.test.tsx | 36 ++-- .../get_render_cell_value.test.tsx | 33 ++-- .../components/table/table.test.tsx | 6 +- .../application/components/table/table.tsx | 3 +- .../generate_csv/generate_csv.test.ts | 1 + .../generate_csv/generate_csv.ts | 2 +- 25 files changed, 292 insertions(+), 326 deletions(-) diff --git a/src/plugins/data/common/search/tabify/__snapshots__/tabify_docs.test.ts.snap b/src/plugins/data/common/search/tabify/__snapshots__/tabify_docs.test.ts.snap index 22276335a0599..9a85ba57ce5ef 100644 --- a/src/plugins/data/common/search/tabify/__snapshots__/tabify_docs.test.ts.snap +++ b/src/plugins/data/common/search/tabify/__snapshots__/tabify_docs.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`tabifyDocs combines meta fields if meta option is set 1`] = ` +exports[`tabify_docs tabifyDocs converts fields by default 1`] = ` Object { "columns": Array [ Object { @@ -108,7 +108,7 @@ Object { } `; -exports[`tabifyDocs converts fields by default 1`] = ` +exports[`tabify_docs tabifyDocs converts source if option is set 1`] = ` Object { "columns": Array [ Object { @@ -216,7 +216,7 @@ Object { } `; -exports[`tabifyDocs converts source if option is set 1`] = ` +exports[`tabify_docs tabifyDocs skips nested fields if option is set 1`] = ` Object { "columns": Array [ Object { @@ -324,115 +324,7 @@ Object { } `; -exports[`tabifyDocs skips nested fields if option is set 1`] = ` -Object { - "columns": Array [ - Object { - "id": "fieldTest", - "meta": Object { - "field": "fieldTest", - "index": "test-index", - "params": Object { - "id": "number", - }, - "type": "number", - }, - "name": "fieldTest", - }, - Object { - "id": "invalidMapping", - "meta": Object { - "field": "invalidMapping", - "index": "test-index", - "params": undefined, - "type": "number", - }, - "name": "invalidMapping", - }, - Object { - "id": "nested", - "meta": Object { - "field": "nested", - "index": "test-index", - "params": undefined, - "type": "object", - }, - "name": "nested", - }, - Object { - "id": "sourceTest", - "meta": Object { - "field": "sourceTest", - "index": "test-index", - "params": Object { - "id": "number", - }, - "type": "number", - }, - "name": "sourceTest", - }, - Object { - "id": "_id", - "meta": Object { - "field": "_id", - "index": "test-index", - "params": undefined, - "type": "string", - }, - "name": "_id", - }, - Object { - "id": "_index", - "meta": Object { - "field": "_index", - "index": "test-index", - "params": undefined, - "type": "string", - }, - "name": "_index", - }, - Object { - "id": "_score", - "meta": Object { - "field": "_score", - "index": "test-index", - "params": undefined, - "type": "number", - }, - "name": "_score", - }, - Object { - "id": "_type", - "meta": Object { - "field": "_type", - "index": "test-index", - "params": undefined, - "type": "string", - }, - "name": "_type", - }, - ], - "rows": Array [ - Object { - "_id": "hit-id-value", - "_index": "hit-index-value", - "_score": 77, - "_type": "hit-type-value", - "fieldTest": 123, - "invalidMapping": 345, - "nested": Array [ - Object { - "field": 123, - }, - ], - "sourceTest": 123, - }, - ], - "type": "datatable", -} -`; - -exports[`tabifyDocs works without provided index pattern 1`] = ` +exports[`tabify_docs tabifyDocs works without provided index pattern 1`] = ` Object { "columns": Array [ Object { @@ -475,53 +367,9 @@ Object { }, "name": "sourceTest", }, - Object { - "id": "_id", - "meta": Object { - "field": "_id", - "index": undefined, - "params": undefined, - "type": "string", - }, - "name": "_id", - }, - Object { - "id": "_index", - "meta": Object { - "field": "_index", - "index": undefined, - "params": undefined, - "type": "string", - }, - "name": "_index", - }, - Object { - "id": "_score", - "meta": Object { - "field": "_score", - "index": undefined, - "params": undefined, - "type": "number", - }, - "name": "_score", - }, - Object { - "id": "_type", - "meta": Object { - "field": "_type", - "index": undefined, - "params": undefined, - "type": "string", - }, - "name": "_type", - }, ], "rows": Array [ Object { - "_id": "hit-id-value", - "_index": "hit-index-value", - "_score": 77, - "_type": "hit-type-value", "fieldTest": 123, "invalidMapping": 345, "nested": Array [ diff --git a/src/plugins/data/common/search/tabify/index.ts b/src/plugins/data/common/search/tabify/index.ts index 74fbc7ba4cfa4..279ff705f231c 100644 --- a/src/plugins/data/common/search/tabify/index.ts +++ b/src/plugins/data/common/search/tabify/index.ts @@ -6,6 +6,6 @@ * Side Public License, v 1. */ -export { tabifyDocs } from './tabify_docs'; +export { tabifyDocs, flattenHit } from './tabify_docs'; export { tabifyAggResponse } from './tabify'; export { tabifyGetColumns } from './get_columns'; diff --git a/src/plugins/data/common/search/tabify/tabify_docs.test.ts b/src/plugins/data/common/search/tabify/tabify_docs.test.ts index 1a3f7195b722e..a2910a1be4a9a 100644 --- a/src/plugins/data/common/search/tabify/tabify_docs.test.ts +++ b/src/plugins/data/common/search/tabify/tabify_docs.test.ts @@ -6,71 +6,137 @@ * Side Public License, v 1. */ -import { tabifyDocs } from './tabify_docs'; -import { IndexPattern } from '../..'; +import { tabifyDocs, flattenHit } from './tabify_docs'; +import { IndexPattern, DataView } from '../..'; import type { estypes } from '@elastic/elasticsearch'; -describe('tabifyDocs', () => { - const fieldFormats = { - getInstance: (id: string) => ({ toJSON: () => ({ id }) }), - getDefaultInstance: (id: string) => ({ toJSON: () => ({ id }) }), - }; +import { fieldFormatsMock } from '../../../../field_formats/common/mocks'; +import { stubbedSavedObjectIndexPattern } from '../../../../data_views/common/data_view.stub'; - const index = new IndexPattern({ +class MockFieldFormatter {} + +fieldFormatsMock.getInstance = jest.fn().mockImplementation(() => new MockFieldFormatter()) as any; + +// helper function to create index patterns +function create(id: string) { + const { + type, + version, + attributes: { timeFieldName, fields, title }, + } = stubbedSavedObjectIndexPattern(id); + + return new DataView({ spec: { - id: 'test-index', - fields: { - sourceTest: { name: 'sourceTest', type: 'number', searchable: true, aggregatable: true }, - fieldTest: { name: 'fieldTest', type: 'number', searchable: true, aggregatable: true }, - 'nested.field': { - name: 'nested.field', - type: 'number', - searchable: true, - aggregatable: true, - }, - }, + id, + type, + version, + timeFieldName, + fields: JSON.parse(fields), + title, + runtimeFieldMap: {}, }, - fieldFormats: fieldFormats as any, + fieldFormats: fieldFormatsMock, + shortDotsEnable: false, + metaFields: ['_id', '_type', '_score', '_routing'], }); +} - // @ts-expect-error not full inteface - const response = { - hits: { - hits: [ +describe('tabify_docs', () => { + describe('flattenHit', () => { + let indexPattern: DataView; + + // create an indexPattern instance for each test + beforeEach(() => { + indexPattern = create('test-pattern'); + }); + + it('returns sorted object keys that combine _source, fields and metaFields in a defined order', () => { + const response = flattenHit( { - _id: 'hit-id-value', - _index: 'hit-index-value', - _type: 'hit-type-value', - _score: 77, - _source: { sourceTest: 123 }, - fields: { fieldTest: 123, invalidMapping: 345, nested: [{ field: 123 }] }, + _index: 'foobar', + _id: 'a', + _source: { + name: 'first', + }, + fields: { + date: ['1'], + zzz: ['z'], + _abc: ['a'], + }, }, - ], - }, - } as estypes.SearchResponse; - - it('converts fields by default', () => { - const table = tabifyDocs(response, index); - expect(table).toMatchSnapshot(); + indexPattern + ); + const expectedOrder = ['_abc', 'date', 'name', 'zzz', '_id', '_routing', '_score', '_type']; + expect(Object.keys(response)).toEqual(expectedOrder); + expect(Object.entries(response).map(([key]) => key)).toEqual(expectedOrder); + }); }); - it('converts source if option is set', () => { - const table = tabifyDocs(response, index, { source: true }); - expect(table).toMatchSnapshot(); - }); + describe('tabifyDocs', () => { + const fieldFormats = { + getInstance: (id: string) => ({ toJSON: () => ({ id }) }), + getDefaultInstance: (id: string) => ({ toJSON: () => ({ id }) }), + }; - it('skips nested fields if option is set', () => { - const table = tabifyDocs(response, index, { shallow: true }); - expect(table).toMatchSnapshot(); - }); + const index = new IndexPattern({ + spec: { + id: 'test-index', + fields: { + sourceTest: { name: 'sourceTest', type: 'number', searchable: true, aggregatable: true }, + fieldTest: { name: 'fieldTest', type: 'number', searchable: true, aggregatable: true }, + 'nested.field': { + name: 'nested.field', + type: 'number', + searchable: true, + aggregatable: true, + }, + }, + }, + metaFields: ['_id', '_index', '_score', '_type'], + fieldFormats: fieldFormats as any, + }); - it('combines meta fields if meta option is set', () => { - const table = tabifyDocs(response, index, { meta: true }); - expect(table).toMatchSnapshot(); - }); + // @ts-expect-error not full inteface + const response = { + hits: { + hits: [ + { + _id: 'hit-id-value', + _index: 'hit-index-value', + _type: 'hit-type-value', + _score: 77, + _source: { sourceTest: 123 }, + fields: { fieldTest: 123, invalidMapping: 345, nested: [{ field: 123 }] }, + }, + ], + }, + } as estypes.SearchResponse; + + it('converts fields by default', () => { + const table = tabifyDocs(response, index); + expect(table).toMatchSnapshot(); + }); + + it('converts source if option is set', () => { + const table = tabifyDocs(response, index, { source: true }); + expect(table).toMatchSnapshot(); + }); + + it('skips nested fields if option is set', () => { + const table = tabifyDocs(response, index, { shallow: true }); + expect(table).toMatchSnapshot(); + }); + + it('combines meta fields from index pattern', () => { + const table = tabifyDocs(response, index); + expect(table.columns.map((col) => col.id)).toEqual( + expect.arrayContaining(['_id', '_index', '_score', '_type']) + ); + }); - it('works without provided index pattern', () => { - const table = tabifyDocs(response); - expect(table).toMatchSnapshot(); + it('works without provided index pattern', () => { + const table = tabifyDocs(response); + expect(table).toMatchSnapshot(); + }); }); }); diff --git a/src/plugins/data/common/search/tabify/tabify_docs.ts b/src/plugins/data/common/search/tabify/tabify_docs.ts index 8e628e7741df5..4259488771761 100644 --- a/src/plugins/data/common/search/tabify/tabify_docs.ts +++ b/src/plugins/data/common/search/tabify/tabify_docs.ts @@ -11,12 +11,60 @@ import { isPlainObject } from 'lodash'; import { IndexPattern } from '../..'; import { Datatable, DatatableColumn, DatatableColumnType } from '../../../../expressions/common'; +type ValidMetaFieldNames = keyof Pick< + estypes.SearchHit, + | '_id' + | '_ignored' + | '_index' + | '_node' + | '_primary_term' + | '_routing' + | '_score' + | '_seq_no' + | '_shard' + | '_source' + | '_type' + | '_version' +>; +const VALID_META_FIELD_NAMES: ValidMetaFieldNames[] = [ + '_id', + '_ignored', + '_index', + '_node', + '_primary_term', + '_routing', + '_score', + '_seq_no', + '_shard', + '_source', + '_type', + '_version', +]; + +function isValidMetaFieldName(field: string): field is ValidMetaFieldNames { + // Since the array above is more narrowly typed than string[], we cannot use + // string to find a value in here. We manually cast it to wider string[] type + // so we're able to use `includes` on it. + return (VALID_META_FIELD_NAMES as string[]).includes(field); +} + export interface TabifyDocsOptions { shallow?: boolean; + /** + * If set to `false` the _source of the document, if requested, won't be + * merged into the flattened document. + */ source?: boolean; - meta?: boolean; } +/** + * Flattens an individual hit (from an ES response) into an object. This will + * create flattened field names, like `user.name`. + * + * @param hit The hit from an ES reponse's hits.hits[] + * @param indexPattern The index pattern for the requested index if available. + * @param params Parameters how to flatten the hit + */ export function flattenHit( hit: estypes.SearchHit, indexPattern?: IndexPattern, @@ -62,13 +110,36 @@ export function flattenHit( if (params?.source !== false && hit._source) { flatten(hit._source as Record); } - if (params?.meta !== false) { - // combine the fields that Discover allows to add as columns - const { _id, _index, _type, _score } = hit; - flatten({ _id, _index, _score, _type }); - } - return flat; + // Merge all valid meta fields into the flattened object + // expect for _source (in case that was specified as a meta field) + indexPattern?.metaFields?.forEach((metaFieldName) => { + if (!isValidMetaFieldName(metaFieldName) || metaFieldName === '_source') { + return; + } + flat[metaFieldName] = hit[metaFieldName]; + }); + + // Use a proxy to make sure that keys are always returned in a specific order, + // so we have a guarantee on the flattened order of keys. + return new Proxy(flat, { + ownKeys: (target) => { + return Reflect.ownKeys(target).sort((a, b) => { + const aIsMeta = indexPattern?.metaFields?.includes(String(a)); + const bIsMeta = indexPattern?.metaFields?.includes(String(b)); + if (aIsMeta && bIsMeta) { + return String(a).localeCompare(String(b)); + } + if (aIsMeta) { + return 1; + } + if (bIsMeta) { + return -1; + } + return String(a).localeCompare(String(b)); + }); + }, + }); } export const tabifyDocs = ( diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 3ae98c083976e..4d51a7ae0ad77 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -52,7 +52,6 @@ import { ILLEGAL_CHARACTERS_VISIBLE, ILLEGAL_CHARACTERS, validateDataView, - flattenHitWrapper, } from './data_views'; export type { IndexPatternsService } from './data_views'; @@ -69,7 +68,6 @@ export const indexPatterns = { getFieldSubtypeMulti, getFieldSubtypeNested, validate: validateDataView, - flattenHitWrapper, }; export { diff --git a/src/plugins/data_views/common/data_views/data_view.ts b/src/plugins/data_views/common/data_views/data_view.ts index 1f038c171abf5..a1b6d4d731bca 100644 --- a/src/plugins/data_views/common/data_views/data_view.ts +++ b/src/plugins/data_views/common/data_views/data_view.ts @@ -72,6 +72,9 @@ export class DataView implements IIndexPattern { formatField: FormatFieldFn; }; public formatField: FormatFieldFn; + /** + * @deprecated Use `flattenHit` utility method exported from data plugin instead. + */ public flattenHit: (hit: Record, deep?: boolean) => Record; public metaFields: string[]; /** diff --git a/src/plugins/data_views/common/data_views/flatten_hit.ts b/src/plugins/data_views/common/data_views/flatten_hit.ts index ddf484affa298..0a6388f0914b1 100644 --- a/src/plugins/data_views/common/data_views/flatten_hit.ts +++ b/src/plugins/data_views/common/data_views/flatten_hit.ts @@ -6,6 +6,11 @@ * Side Public License, v 1. */ +// --------- DEPRECATED --------- +// This implementation of flattenHit is deprecated and should no longer be used. +// If you consider adding features to this, please don't but use the `flattenHit` +// implementation from the data plugin. + import _ from 'lodash'; import { DataView } from './data_view'; @@ -114,15 +119,3 @@ export function flattenHitWrapper(dataView: DataView, metaFields = {}, cache = n return decorateFlattened(flattened); }; } - -/** - * This wraps `flattenHitWrapper` so one single cache can be provided for all uses of that - * function. The returned value of this function is what is included in the index patterns - * setup contract. - * - * @public - */ -export function createFlattenHitWrapper() { - const cache = new WeakMap(); - return _.partial(flattenHitWrapper, _, _, cache); -} diff --git a/src/plugins/data_views/public/index.ts b/src/plugins/data_views/public/index.ts index 6b67da68ed78e..d8814e886fb0e 100644 --- a/src/plugins/data_views/public/index.ts +++ b/src/plugins/data_views/public/index.ts @@ -14,7 +14,6 @@ export { validateDataView, } from '../common/lib'; export { - flattenHitWrapper, formatHitProvider, onRedirectNoIndexPattern, onUnsupportedTimePattern, diff --git a/src/plugins/discover/public/__mocks__/index_pattern.ts b/src/plugins/discover/public/__mocks__/index_pattern.ts index f9cc202f9063e..2acb512617a6b 100644 --- a/src/plugins/discover/public/__mocks__/index_pattern.ts +++ b/src/plugins/discover/public/__mocks__/index_pattern.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -import { IIndexPatternFieldList } from '../../../data/common'; +import type { estypes } from '@elastic/elasticsearch'; +import { flattenHit, IIndexPatternFieldList } from '../../../data/common'; import { IndexPattern } from '../../../data/common'; -import { indexPatterns } from '../../../data/public'; const fields = [ { @@ -85,10 +85,11 @@ const indexPattern = { getFormatterForField: () => ({ convert: () => 'formatted' }), } as unknown as IndexPattern; -indexPattern.flattenHit = indexPatterns.flattenHitWrapper(indexPattern, indexPattern.metaFields); indexPattern.isTimeBased = () => !!indexPattern.timeFieldName; indexPattern.formatField = (hit: Record, fieldName: string) => { - return fieldName === '_source' ? hit._source : indexPattern.flattenHit(hit)[fieldName]; + return fieldName === '_source' + ? hit._source + : flattenHit(hit as unknown as estypes.SearchHit, indexPattern)[fieldName]; }; export const indexPatternMock = indexPattern; diff --git a/src/plugins/discover/public/__mocks__/index_pattern_with_timefield.ts b/src/plugins/discover/public/__mocks__/index_pattern_with_timefield.ts index f1f1d74f3af3a..ac30c24ef6f08 100644 --- a/src/plugins/discover/public/__mocks__/index_pattern_with_timefield.ts +++ b/src/plugins/discover/public/__mocks__/index_pattern_with_timefield.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -import { IIndexPatternFieldList } from '../../../data/common'; +import { flattenHit, IIndexPatternFieldList } from '../../../data/common'; import { IndexPattern } from '../../../data/common'; -import { indexPatterns } from '../../../data/public'; +import type { estypes } from '@elastic/elasticsearch'; const fields = [ { @@ -74,10 +74,11 @@ const indexPattern = { getFormatterForField: () => ({ convert: () => 'formatted' }), } as unknown as IndexPattern; -indexPattern.flattenHit = indexPatterns.flattenHitWrapper(indexPattern, indexPattern.metaFields); indexPattern.isTimeBased = () => !!indexPattern.timeFieldName; indexPattern.formatField = (hit: Record, fieldName: string) => { - return fieldName === '_source' ? hit._source : indexPattern.flattenHit(hit)[fieldName]; + return fieldName === '_source' + ? hit._source + : flattenHit(hit as unknown as estypes.SearchHit, indexPattern)[fieldName]; }; export const indexPatternWithTimefieldMock = indexPattern; diff --git a/src/plugins/discover/public/application/apps/main/components/doc_table/components/table_row.tsx b/src/plugins/discover/public/application/apps/main/components/doc_table/components/table_row.tsx index 8d56f2adeaf65..d91735460af08 100644 --- a/src/plugins/discover/public/application/apps/main/components/doc_table/components/table_row.tsx +++ b/src/plugins/discover/public/application/apps/main/components/doc_table/components/table_row.tsx @@ -10,6 +10,7 @@ import React, { Fragment, useCallback, useMemo, useState } from 'react'; import classNames from 'classnames'; import { i18n } from '@kbn/i18n'; import { EuiButtonEmpty, EuiIcon } from '@elastic/eui'; +import { flattenHit } from '../../../../../../../../data/common'; import { DocViewer } from '../../../../../components/doc_viewer/doc_viewer'; import { FilterManager, IndexPattern } from '../../../../../../../../data/public'; import { TableCell } from './table_row/table_cell'; @@ -57,7 +58,7 @@ export const TableRow = ({ }); const anchorDocTableRowSubj = row.isAnchor ? ' docTableAnchorRow' : ''; - const flattenedRow = useMemo(() => indexPattern.flattenHit(row), [indexPattern, row]); + const flattenedRow = useMemo(() => flattenHit(row, indexPattern), [indexPattern, row]); const mapping = useMemo(() => indexPattern.fields.getByName, [indexPattern]); // toggle display of the rows details, a full list of the fields from each row diff --git a/src/plugins/discover/public/application/apps/main/components/sidebar/discover_sidebar.test.tsx b/src/plugins/discover/public/application/apps/main/components/sidebar/discover_sidebar.test.tsx index e53bf006e2b4e..a550dbd59b9fa 100644 --- a/src/plugins/discover/public/application/apps/main/components/sidebar/discover_sidebar.test.tsx +++ b/src/plugins/discover/public/application/apps/main/components/sidebar/discover_sidebar.test.tsx @@ -15,7 +15,7 @@ import realHits from '../../../../../__fixtures__/real_hits.js'; import { mountWithIntl } from '@kbn/test/jest'; import React from 'react'; import { DiscoverSidebarProps } from './discover_sidebar'; -import { IndexPatternAttributes } from '../../../../../../../data/common'; +import { flattenHit, IndexPatternAttributes } from '../../../../../../../data/common'; import { SavedObject } from '../../../../../../../../core/types'; import { getDefaultFieldFilter } from './lib/field_filter'; import { DiscoverSidebarComponent as DiscoverSidebar } from './discover_sidebar'; @@ -44,7 +44,7 @@ function getCompProps(): DiscoverSidebarProps { const fieldCounts: Record = {}; for (const hit of hits) { - for (const key of Object.keys(indexPattern.flattenHit(hit))) { + for (const key of Object.keys(flattenHit(hit, indexPattern))) { fieldCounts[key] = (fieldCounts[key] || 0) + 1; } } diff --git a/src/plugins/discover/public/application/apps/main/components/sidebar/discover_sidebar_responsive.test.tsx b/src/plugins/discover/public/application/apps/main/components/sidebar/discover_sidebar_responsive.test.tsx index 9d73f885c988d..ded7897d2a9e5 100644 --- a/src/plugins/discover/public/application/apps/main/components/sidebar/discover_sidebar_responsive.test.tsx +++ b/src/plugins/discover/public/application/apps/main/components/sidebar/discover_sidebar_responsive.test.tsx @@ -15,7 +15,7 @@ import realHits from '../../../../../__fixtures__/real_hits.js'; import { act } from 'react-dom/test-utils'; import { mountWithIntl } from '@kbn/test/jest'; import React from 'react'; -import { IndexPatternAttributes } from '../../../../../../../data/common'; +import { flattenHit, IndexPatternAttributes } from '../../../../../../../data/common'; import { SavedObject } from '../../../../../../../../core/types'; import { DiscoverSidebarResponsive, @@ -72,7 +72,7 @@ function getCompProps(): DiscoverSidebarResponsiveProps { const indexPattern = stubLogstashIndexPattern; // @ts-expect-error _.each() is passing additional args to flattenHit - const hits = each(cloneDeep(realHits), indexPattern.flattenHit) as Array< + const hits = each(cloneDeep(realHits), (hit) => flattenHit(hit, indexPattern)) as Array< Record > as ElasticSearchHit[]; @@ -83,7 +83,7 @@ function getCompProps(): DiscoverSidebarResponsiveProps { ]; for (const hit of hits) { - for (const key of Object.keys(indexPattern.flattenHit(hit))) { + for (const key of Object.keys(flattenHit(hit, indexPattern))) { mockfieldCounts[key] = (mockfieldCounts[key] || 0) + 1; } } diff --git a/src/plugins/discover/public/application/apps/main/components/sidebar/lib/field_calculator.js b/src/plugins/discover/public/application/apps/main/components/sidebar/lib/field_calculator.js index 8f86cdad82cf7..be7e9c616273d 100644 --- a/src/plugins/discover/public/application/apps/main/components/sidebar/lib/field_calculator.js +++ b/src/plugins/discover/public/application/apps/main/components/sidebar/lib/field_calculator.js @@ -8,12 +8,12 @@ import { map, sortBy, without, each, defaults, isObject } from 'lodash'; import { i18n } from '@kbn/i18n'; +import { flattenHit } from '../../../../../../../../data/common'; function getFieldValues(hits, field, indexPattern) { const name = field.name; - const flattenHit = indexPattern.flattenHit; return map(hits, function (hit) { - return flattenHit(hit)[name]; + return flattenHit(hit, indexPattern)[name]; }); } diff --git a/src/plugins/discover/public/application/apps/main/components/sidebar/lib/field_calculator.test.ts b/src/plugins/discover/public/application/apps/main/components/sidebar/lib/field_calculator.test.ts index c3ff7970c5aac..d4bc41f36b2d4 100644 --- a/src/plugins/discover/public/application/apps/main/components/sidebar/lib/field_calculator.test.ts +++ b/src/plugins/discover/public/application/apps/main/components/sidebar/lib/field_calculator.test.ts @@ -13,6 +13,7 @@ import { keys, each, cloneDeep, clone, uniq, filter, map } from 'lodash'; import realHits from '../../../../../../__fixtures__/real_hits.js'; import { IndexPattern } from '../../../../../../../../data/public'; +import { flattenHit } from '../../../../../../../../data/common'; // @ts-expect-error import { fieldCalculator } from './field_calculator'; @@ -120,7 +121,7 @@ describe('fieldCalculator', function () { let hits: any; beforeEach(function () { - hits = each(cloneDeep(realHits), (hit) => indexPattern.flattenHit(hit)); + hits = each(cloneDeep(realHits), (hit) => flattenHit(hit, indexPattern)); }); it('Should return an array of values for _source fields', function () { diff --git a/src/plugins/discover/public/application/apps/main/utils/calc_field_counts.ts b/src/plugins/discover/public/application/apps/main/utils/calc_field_counts.ts index 1ce7023539be4..211c4e5c8b069 100644 --- a/src/plugins/discover/public/application/apps/main/utils/calc_field_counts.ts +++ b/src/plugins/discover/public/application/apps/main/utils/calc_field_counts.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { IndexPattern } from 'src/plugins/data/common'; +import { flattenHit, IndexPattern } from '../../../../../../data/common'; import { ElasticSearchHit } from '../../../doc_views/doc_views_types'; /** @@ -22,7 +22,7 @@ export function calcFieldCounts( return {}; } for (const hit of rows) { - const fields = Object.keys(indexPattern.flattenHit(hit)); + const fields = Object.keys(flattenHit(hit, indexPattern)); for (const fieldName of fields) { counts[fieldName] = (counts[fieldName] || 0) + 1; } diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid.tsx index 0fe506b3b8537..11323080274a9 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid.tsx @@ -21,7 +21,7 @@ import { EuiLoadingSpinner, EuiIcon, } from '@elastic/eui'; -import type { IndexPattern } from 'src/plugins/data/common'; +import { flattenHit, IndexPattern } from '../../../../../data/common'; import { DocViewFilterFn, ElasticSearchHit } from '../../doc_views/doc_views_types'; import { getSchemaDetectors } from './discover_grid_schema'; import { DiscoverGridFlyout } from './discover_grid_flyout'; @@ -271,7 +271,7 @@ export const DiscoverGrid = ({ getRenderCellValueFn( indexPattern, displayedRows, - displayedRows ? displayedRows.map((hit) => indexPattern.flattenHit(hit)) : [], + displayedRows ? displayedRows.map((hit) => flattenHit(hit, indexPattern)) : [], useNewFieldsApi, fieldsToShow, services.uiSettings.get(MAX_DOC_FIELDS_DISPLAYED) diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.tsx index b1823eb3d668c..a31b551821ddb 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.tsx @@ -9,7 +9,7 @@ import React, { useContext } from 'react'; import { EuiDataGridColumnCellActionProps } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { IndexPatternField } from '../../../../../data/common'; +import { flattenHit, IndexPatternField } from '../../../../../data/common'; import { DiscoverGridContext } from './discover_grid_context'; export const FilterInBtn = ({ @@ -27,7 +27,7 @@ export const FilterInBtn = ({ { const row = context.rows[rowIndex]; - const flattened = context.indexPattern.flattenHit(row); + const flattened = flattenHit(row, context.indexPattern); if (flattened) { context.onFilter(columnId, flattened[columnId], '+'); @@ -60,7 +60,7 @@ export const FilterOutBtn = ({ { const row = context.rows[rowIndex]; - const flattened = context.indexPattern.flattenHit(row); + const flattened = flattenHit(row, context.indexPattern); if (flattened) { context.onFilter(columnId, flattened[columnId], '-'); diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_document_selection.test.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_document_selection.test.tsx index 41cf3f5a68edb..e9b93e21553a2 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_document_selection.test.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_document_selection.test.tsx @@ -17,6 +17,17 @@ import { esHits } from '../../../__mocks__/es_hits'; import { indexPatternMock } from '../../../__mocks__/index_pattern'; import { DiscoverGridContext } from './discover_grid_context'; +const baseContextMock = { + expanded: undefined, + setExpanded: jest.fn(), + rows: esHits, + onFilter: jest.fn(), + indexPattern: indexPatternMock, + isDarkMode: false, + selectedDocs: [], + setSelectedDocs: jest.fn(), +}; + describe('document selection', () => { describe('getDocId', () => { test('doc with custom routing', () => { @@ -39,14 +50,7 @@ describe('document selection', () => { describe('SelectButton', () => { test('is not checked', () => { const contextMock = { - expanded: undefined, - setExpanded: jest.fn(), - rows: esHits, - onFilter: jest.fn(), - indexPattern: indexPatternMock, - isDarkMode: false, - selectedDocs: [], - setSelectedDocs: jest.fn(), + ...baseContextMock, }; const component = mountWithIntl( @@ -68,14 +72,8 @@ describe('document selection', () => { test('is checked', () => { const contextMock = { - expanded: undefined, - setExpanded: jest.fn(), - rows: esHits, - onFilter: jest.fn(), - indexPattern: indexPatternMock, - isDarkMode: false, + ...baseContextMock, selectedDocs: ['i::1::'], - setSelectedDocs: jest.fn(), }; const component = mountWithIntl( @@ -97,14 +95,7 @@ describe('document selection', () => { test('adding a selection', () => { const contextMock = { - expanded: undefined, - setExpanded: jest.fn(), - rows: esHits, - onFilter: jest.fn(), - indexPattern: indexPatternMock, - isDarkMode: false, - selectedDocs: [], - setSelectedDocs: jest.fn(), + ...baseContextMock, }; const component = mountWithIntl( @@ -126,14 +117,8 @@ describe('document selection', () => { }); test('removing a selection', () => { const contextMock = { - expanded: undefined, - setExpanded: jest.fn(), - rows: esHits, - onFilter: jest.fn(), - indexPattern: indexPatternMock, - isDarkMode: false, + ...baseContextMock, selectedDocs: ['i::1::'], - setSelectedDocs: jest.fn(), }; const component = mountWithIntl( diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.test.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.test.tsx index d1299b39a25b2..3f7cb70091cfa 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.test.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.test.tsx @@ -14,17 +14,21 @@ import { DiscoverGridContext } from './discover_grid_context'; import { indexPatternMock } from '../../../__mocks__/index_pattern'; import { esHits } from '../../../__mocks__/es_hits'; +const baseContextMock = { + expanded: undefined, + setExpanded: jest.fn(), + rows: esHits, + onFilter: jest.fn(), + indexPattern: indexPatternMock, + isDarkMode: false, + selectedDocs: [], + setSelectedDocs: jest.fn(), +}; + describe('Discover grid view button ', function () { it('when no document is expanded, setExpanded is called with current document', async () => { const contextMock = { - expanded: undefined, - setExpanded: jest.fn(), - rows: esHits, - onFilter: jest.fn(), - indexPattern: indexPatternMock, - isDarkMode: false, - selectedDocs: [], - setSelectedDocs: jest.fn(), + ...baseContextMock, }; const component = mountWithIntl( @@ -45,14 +49,8 @@ describe('Discover grid view button ', function () { }); it('when the current document is expanded, setExpanded is called with undefined', async () => { const contextMock = { + ...baseContextMock, expanded: esHits[0], - setExpanded: jest.fn(), - rows: esHits, - onFilter: jest.fn(), - indexPattern: indexPatternMock, - isDarkMode: false, - selectedDocs: [], - setSelectedDocs: jest.fn(), }; const component = mountWithIntl( @@ -73,14 +71,8 @@ describe('Discover grid view button ', function () { }); it('when another document is expanded, setExpanded is called with the current document', async () => { const contextMock = { + ...baseContextMock, expanded: esHits[0], - setExpanded: jest.fn(), - rows: esHits, - onFilter: jest.fn(), - indexPattern: indexPatternMock, - isDarkMode: false, - selectedDocs: [], - setSelectedDocs: jest.fn(), }; const component = mountWithIntl( diff --git a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.test.tsx b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.test.tsx index 5aca237d46581..6556876217953 100644 --- a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.test.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.test.tsx @@ -11,6 +11,7 @@ import { ReactWrapper, shallow } from 'enzyme'; import { getRenderCellValueFn } from './get_render_cell_value'; import { indexPatternMock } from '../../../__mocks__/index_pattern'; import { ElasticSearchHit } from '../../doc_views/doc_views_types'; +import { flattenHit } from 'src/plugins/data/common'; jest.mock('../../../../../kibana_react/public', () => ({ useUiSetting: () => true, @@ -68,12 +69,16 @@ const rowsFieldsWithTopLevelObject: ElasticSearchHit[] = [ }, ]; +const flatten = (hit: ElasticSearchHit): Record => { + return flattenHit(hit, indexPatternMock); +}; + describe('Discover grid cell rendering', function () { it('renders bytes column correctly', () => { const DiscoverGridCellValue = getRenderCellValueFn( indexPatternMock, rowsSource, - rowsSource.map((row) => indexPatternMock.flattenHit(row)), + rowsSource.map(flatten), false, [], 100 @@ -95,7 +100,7 @@ describe('Discover grid cell rendering', function () { const DiscoverGridCellValue = getRenderCellValueFn( indexPatternMock, rowsSource, - rowsSource.map((row) => indexPatternMock.flattenHit(row)), + rowsSource.map(flatten), false, [], 100 @@ -146,7 +151,7 @@ describe('Discover grid cell rendering', function () { const DiscoverGridCellValue = getRenderCellValueFn( indexPatternMock, rowsSource, - rowsSource.map((row) => indexPatternMock.flattenHit(row)), + rowsSource.map(flatten), false, [], 100 @@ -189,7 +194,7 @@ describe('Discover grid cell rendering', function () { const DiscoverGridCellValue = getRenderCellValueFn( indexPatternMock, rowsFields, - rowsFields.map((row) => indexPatternMock.flattenHit(row)), + rowsFields.map(flatten), true, [], 100 @@ -244,7 +249,7 @@ describe('Discover grid cell rendering', function () { const DiscoverGridCellValue = getRenderCellValueFn( indexPatternMock, rowsFields, - rowsFields.map((row) => indexPatternMock.flattenHit(row)), + rowsFields.map(flatten), true, [], // this is the number of rendered items @@ -287,7 +292,7 @@ describe('Discover grid cell rendering', function () { const DiscoverGridCellValue = getRenderCellValueFn( indexPatternMock, rowsFields, - rowsFields.map((row) => indexPatternMock.flattenHit(row)), + rowsFields.map(flatten), true, [], 100 @@ -335,7 +340,7 @@ describe('Discover grid cell rendering', function () { const DiscoverGridCellValue = getRenderCellValueFn( indexPatternMock, rowsFieldsWithTopLevelObject, - rowsFieldsWithTopLevelObject.map((row) => indexPatternMock.flattenHit(row)), + rowsFieldsWithTopLevelObject.map(flatten), true, [], 100 @@ -376,7 +381,7 @@ describe('Discover grid cell rendering', function () { const DiscoverGridCellValue = getRenderCellValueFn( indexPatternMock, rowsFieldsWithTopLevelObject, - rowsFieldsWithTopLevelObject.map((row) => indexPatternMock.flattenHit(row)), + rowsFieldsWithTopLevelObject.map(flatten), true, [], 100 @@ -416,7 +421,7 @@ describe('Discover grid cell rendering', function () { const DiscoverGridCellValue = getRenderCellValueFn( indexPatternMock, rowsFieldsWithTopLevelObject, - rowsFieldsWithTopLevelObject.map((row) => indexPatternMock.flattenHit(row)), + rowsFieldsWithTopLevelObject.map(flatten), true, [], 100 @@ -447,7 +452,7 @@ describe('Discover grid cell rendering', function () { const DiscoverGridCellValue = getRenderCellValueFn( indexPatternMock, rowsFieldsWithTopLevelObject, - rowsFieldsWithTopLevelObject.map((row) => indexPatternMock.flattenHit(row)), + rowsFieldsWithTopLevelObject.map(flatten), true, [], 100 @@ -466,7 +471,9 @@ describe('Discover grid cell rendering', function () { @@ -477,7 +484,7 @@ describe('Discover grid cell rendering', function () { const DiscoverGridCellValue = getRenderCellValueFn( indexPatternMock, rowsSource, - rowsSource.map((row) => indexPatternMock.flattenHit(row)), + rowsSource.map(flatten), false, [], 100 @@ -499,7 +506,7 @@ describe('Discover grid cell rendering', function () { const DiscoverGridCellValue = getRenderCellValueFn( indexPatternMock, rowsSource, - rowsSource.map((row) => indexPatternMock.flattenHit(row)), + rowsSource.map(flatten), false, [], 100 diff --git a/src/plugins/discover/public/application/components/table/table.test.tsx b/src/plugins/discover/public/application/components/table/table.test.tsx index 3f010d9d07737..ce914edcec703 100644 --- a/src/plugins/discover/public/application/components/table/table.test.tsx +++ b/src/plugins/discover/public/application/components/table/table.test.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; import { findTestSubject } from '@elastic/eui/lib/test'; import { DocViewerTable, DocViewerTableProps } from './table'; -import { indexPatterns, IndexPattern } from '../../../../../data/public'; +import { IndexPattern } from '../../../../../data/public'; import { ElasticSearchHit } from '../../doc_views/doc_views_types'; jest.mock('../../../kibana_services', () => ({ @@ -65,7 +65,7 @@ const indexPattern = { ], }, metaFields: ['_index', '_score'], - flattenHit: undefined, + flattenHit: jest.fn(), formatHit: jest.fn((hit) => hit._source), } as unknown as IndexPattern; @@ -73,8 +73,6 @@ indexPattern.fields.getByName = (name: string) => { return indexPattern.fields.getAll().find((field) => field.name === name); }; -indexPattern.flattenHit = indexPatterns.flattenHitWrapper(indexPattern, indexPattern.metaFields); - const mountComponent = (props: DocViewerTableProps) => { return mountWithIntl(); }; diff --git a/src/plugins/discover/public/application/components/table/table.tsx b/src/plugins/discover/public/application/components/table/table.tsx index eab3ba6e3d29a..7f597d846f88f 100644 --- a/src/plugins/discover/public/application/components/table/table.tsx +++ b/src/plugins/discover/public/application/components/table/table.tsx @@ -9,6 +9,7 @@ import React, { useCallback, useMemo } from 'react'; import { EuiInMemoryTable } from '@elastic/eui'; import { IndexPattern, IndexPatternField } from '../../../../../data/public'; +import { flattenHit } from '../../../../../data/common'; import { SHOW_MULTIFIELDS } from '../../../../common'; import { getServices } from '../../../kibana_services'; import { isNestedFieldParent } from '../../apps/main/utils/nested_fields'; @@ -95,7 +96,7 @@ export const DocViewerTable = ({ return null; } - const flattened = indexPattern?.flattenHit(hit); + const flattened = flattenHit(hit, indexPattern, { source: true }); const fieldsToShow = getFieldsToShow(Object.keys(flattened), indexPattern, showMultiFields); const items: FieldRecord[] = Object.keys(flattened) diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts index f393661e4c490..1902c4ed0272e 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts @@ -75,6 +75,7 @@ const mockSearchSourceGetFieldDefault = jest.fn().mockImplementation((key: strin getByName: jest.fn().mockImplementation(() => []), getByType: jest.fn().mockImplementation(() => []), }, + metaFields: ['_id', '_index', '_type', '_score'], getFormatterForField: jest.fn(), }; } diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts index c269677ae930d..6c2989d54309d 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts @@ -356,7 +356,7 @@ export class CsvGenerator { let table: Datatable | undefined; try { - table = tabifyDocs(results, index, { shallow: true, meta: true }); + table = tabifyDocs(results, index, { shallow: true }); } catch (err) { this.logger.error(err); }