Deprecate DataView.flattenHit in favor of data plugin flattenHit (#114517)

* 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>
This commit is contained in:
Tim Roes 2021-10-14 14:43:06 +02:00 committed by GitHub
parent 29d750a8c1
commit b21e1ebf38
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 298 additions and 331 deletions

View file

@ -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 [

View file

@ -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';

View file

@ -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: fieldFormatsMock,
shortDotsEnable: false,
metaFields: ['_id', '_type', '_score', '_routing'],
});
}
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(
{
_index: 'foobar',
_id: 'a',
_source: {
name: 'first',
},
fields: {
date: ['1'],
zzz: ['z'],
_abc: ['a'],
},
},
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);
});
});
describe('tabifyDocs', () => {
const fieldFormats = {
getInstance: (id: string) => ({ toJSON: () => ({ id }) }),
getDefaultInstance: (id: string) => ({ toJSON: () => ({ id }) }),
};
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,
},
},
},
},
fieldFormats: fieldFormats as any,
});
metaFields: ['_id', '_index', '_score', '_type'],
fieldFormats: fieldFormats as any,
});
// @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<unknown>;
// @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<unknown>;
it('converts fields by default', () => {
const table = tabifyDocs(response, index);
expect(table).toMatchSnapshot();
});
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('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('skips nested fields if option is set', () => {
const table = tabifyDocs(response, index, { shallow: true });
expect(table).toMatchSnapshot();
});
it('combines meta fields if meta option is set', () => {
const table = tabifyDocs(response, index, { meta: 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();
});
});
});

View file

@ -11,12 +11,60 @@ import { isPlainObject } from 'lodash';
import { IndexPattern } from '../..';
import { Datatable, DatatableColumn, DatatableColumnType } from '../../../../expressions/common';
export interface TabifyDocsOptions {
shallow?: boolean;
source?: boolean;
meta?: boolean;
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;
}
/**
* 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<string, any>);
}
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 = (

View file

@ -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 {

View file

@ -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<string, any>, deep?: boolean) => Record<string, any>;
public metaFields: string[];
/**

View file

@ -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);
}

View file

@ -13,7 +13,7 @@ export {
ILLEGAL_CHARACTERS,
validateDataView,
} from '../common/lib';
export { flattenHitWrapper, formatHitProvider, onRedirectNoIndexPattern } from './data_views';
export { formatHitProvider, onRedirectNoIndexPattern } from './data_views';
export { IndexPatternField, IIndexPatternFieldList, TypeMeta } from '../common';

View file

@ -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<string, unknown>, 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;

View file

@ -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 = [
{
@ -76,10 +76,11 @@ const indexPattern = {
popularizeField: () => {},
} as unknown as IndexPattern;
indexPattern.flattenHit = indexPatterns.flattenHitWrapper(indexPattern, indexPattern.metaFields);
indexPattern.isTimeBased = () => !!indexPattern.timeFieldName;
indexPattern.formatField = (hit: Record<string, unknown>, 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;

View file

@ -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

View file

@ -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<string, number> = {};
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;
}
}

View file

@ -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<string, unknown>
> 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;
}
}

View file

@ -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];
});
}

View file

@ -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 () {

View file

@ -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;
}

View file

@ -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)

View file

@ -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 = ({
<Component
onClick={() => {
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 = ({
<Component
onClick={() => {
const row = context.rows[rowIndex];
const flattened = context.indexPattern.flattenHit(row);
const flattened = flattenHit(row, context.indexPattern);
if (flattened) {
context.onFilter(columnId, flattened[columnId], '-');

View file

@ -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(

View file

@ -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(

View file

@ -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<string, unknown> => {
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 () {
<span
dangerouslySetInnerHTML={
Object {
"__html": 100,
"__html": Array [
100,
],
}
}
/>
@ -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

View file

@ -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(<DocViewerTable {...props} />);
};

View file

@ -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)

View file

@ -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(),
};
}

View file

@ -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);
}