Skip to content

Commit

Permalink
[vis_filters]: Convert to TS/Jest, write tests (#58488) (#59276)
Browse files Browse the repository at this point in the history
* Converted files to TS. Added tests.

* Removed ts-ignore

* Fixed comments and tests

* Fixed error

* Fixed tests

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>

Co-authored-by: Uladzislau Lasitsa <vlad.lasitsa@gmail.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
3 people authored Mar 4, 2020
1 parent a9c24bf commit 461eea4
Show file tree
Hide file tree
Showing 7 changed files with 330 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import {
fieldFormats,
FieldFormatsGetConfigFn,
esFilters,
IndexPatternsContract,
} from '../../../../../../plugins/data/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { setIndexPatterns } from '../../../../../../plugins/data/public/services';
import { dataPluginMock } from '../../../../../../plugins/data/public/mocks';
import { createFiltersFromEvent, EventData } from './create_filters_from_event';
import { mockDataServices } from '../../search/aggs/test_helpers';

jest.mock('ui/new_platform');

const mockField = {
name: 'bytes',
indexPattern: {
id: 'logstash-*',
},
filterable: true,
format: new fieldFormats.BytesFormat({}, (() => {}) as FieldFormatsGetConfigFn),
};

describe('createFiltersFromEvent', () => {
let dataPoints: EventData[];

beforeEach(() => {
dataPoints = [
{
table: {
columns: [
{
name: 'test',
id: '1-1',
meta: {
type: 'histogram',
indexPatternId: 'logstash-*',
aggConfigParams: {
field: 'bytes',
interval: 30,
otherBucket: true,
},
},
},
],
rows: [
{
'1-1': '2048',
},
],
},
column: 0,
row: 0,
value: 'test',
},
];

mockDataServices();
setIndexPatterns(({
...dataPluginMock.createStartContract().indexPatterns,
get: async () => ({
id: 'logstash-*',
fields: {
getByName: () => mockField,
filter: () => [mockField],
},
}),
} as unknown) as IndexPatternsContract);
});

test('ignores event when value for rows is not provided', async () => {
dataPoints[0].table.rows[0]['1-1'] = null;
const filters = await createFiltersFromEvent(dataPoints);

expect(filters.length).toEqual(0);
});

test('handles an event when aggregations type is a terms', async () => {
if (dataPoints[0].table.columns[0].meta) {
dataPoints[0].table.columns[0].meta.type = 'terms';
}
const filters = await createFiltersFromEvent(dataPoints);

expect(filters.length).toEqual(1);
expect(filters[0].query.match_phrase.bytes).toEqual('2048');
});

test('handles an event when aggregations type is not terms', async () => {
const filters = await createFiltersFromEvent(dataPoints);

expect(filters.length).toEqual(1);

const [rangeFilter] = filters;

if (esFilters.isRangeFilter(rangeFilter)) {
expect(rangeFilter.range.bytes.gte).toEqual(2048);
expect(rangeFilter.range.bytes.lt).toEqual(2078);
}
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,33 @@
* under the License.
*/

import { esFilters } from '../../../../../../plugins/data/public';
import { KibanaDatatable } from '../../../../../../plugins/expressions/public';
import { esFilters, Filter } from '../../../../../../plugins/data/public';
import { deserializeAggConfig } from '../../search/expressions/utils';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getIndexPatterns } from '../../../../../../plugins/data/public/services';

export interface EventData {
table: Pick<KibanaDatatable, 'rows' | 'columns'>;
column: number;
row: number;
value: any;
}

/**
* For terms aggregations on `__other__` buckets, this assembles a list of applicable filter
* terms based on a specific cell in the tabified data.
*
* @param {object} table - tabified table data
* @param {EventData['table']} table - tabified table data
* @param {number} columnIndex - current column index
* @param {number} rowIndex - current row index
* @return {array} - array of terms to filter against
*/
const getOtherBucketFilterTerms = (table, columnIndex, rowIndex) => {
const getOtherBucketFilterTerms = (
table: EventData['table'],
columnIndex: number,
rowIndex: number
) => {
if (rowIndex === -1) {
return [];
}
Expand All @@ -42,7 +54,7 @@ const getOtherBucketFilterTerms = (table, columnIndex, rowIndex) => {
return row[column.id] === table.rows[rowIndex][column.id] || i >= columnIndex;
});
});
const terms = rows.map(row => row[table.columns[columnIndex].id]);
const terms: any[] = rows.map(row => row[table.columns[columnIndex].id]);

return [
...new Set(
Expand All @@ -59,22 +71,27 @@ const getOtherBucketFilterTerms = (table, columnIndex, rowIndex) => {
* Assembles the filters needed to apply filtering against a specific cell value, while accounting
* for cases like if the value is a terms agg in an `__other__` or `__missing__` bucket.
*
* @param {object} table - tabified table data
* @param {EventData['table']} table - tabified table data
* @param {number} columnIndex - current column index
* @param {number} rowIndex - current row index
* @param {string} cellValue - value of the current cell
* @return {array|string} - filter or list of filters to provide to queryFilter.addFilters()
* @return {Filter[]|undefined} - list of filters to provide to queryFilter.addFilters()
*/
const createFilter = async (table, columnIndex, rowIndex) => {
if (!table || !table.columns || !table.columns[columnIndex]) return;
const createFilter = async (table: EventData['table'], columnIndex: number, rowIndex: number) => {
if (!table || !table.columns || !table.columns[columnIndex]) {
return;
}
const column = table.columns[columnIndex];
if (!column.meta || !column.meta.indexPatternId) {
return;
}
const aggConfig = deserializeAggConfig({
type: column.meta.type,
aggConfigParams: column.meta.aggConfigParams,
aggConfigParams: column.meta.aggConfigParams ? column.meta.aggConfigParams : {},
indexPattern: await getIndexPatterns().get(column.meta.indexPatternId),
});
let filter = [];
const value = rowIndex > -1 ? table.rows[rowIndex][column.id] : null;
let filter: Filter[] = [];
const value: any = rowIndex > -1 ? table.rows[rowIndex][column.id] : null;
if (value === null || value === undefined || !aggConfig.isFilterable()) {
return;
}
Expand All @@ -85,26 +102,29 @@ const createFilter = async (table, columnIndex, rowIndex) => {
filter = aggConfig.createFilter(value);
}

if (!filter) {
return;
}

if (!Array.isArray(filter)) {
filter = [filter];
}

return filter;
};

const createFiltersFromEvent = async event => {
const filters = [];
const dataPoints = event.data || [event];
const createFiltersFromEvent = async (dataPoints: EventData[], negate?: boolean) => {
const filters: Filter[] = [];

await Promise.all(
dataPoints
.filter(point => point)
.map(async val => {
const { table, column, row } = val;
const filter = await createFilter(table, column, row);
const filter: Filter[] = (await createFilter(table, column, row)) || [];
if (filter) {
filter.forEach(f => {
if (event.negate) {
if (negate) {
f = esFilters.toggleFilterNegated(f);
}
filters.push(f);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ interface ActionContext {

async function isCompatible(context: ActionContext) {
try {
const filters: Filter[] = (await createFiltersFromEvent(context.data)) || [];
const filters: Filter[] =
(await createFiltersFromEvent(context.data.data || [context.data], context.data.negate)) ||
[];
return filters.length > 0;
} catch {
return false;
Expand All @@ -71,7 +73,8 @@ export function valueClickAction(
throw new IncompatibleActionError();
}

const filters: Filter[] = (await createFiltersFromEvent(data)) || [];
const filters: Filter[] =
(await createFiltersFromEvent(data.data || [data], data.negate)) || [];

let selectedFilters: Filter[] = esFilters.mapAndFlattenFilters(filters);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import { set } from 'lodash';
// @ts-ignore
import { FormattedData } from '../../../../../../plugins/inspector/public';
// @ts-ignore

import { createFilter } from './create_filter';

import { TabbedTable } from '../tabify';
Expand Down Expand Up @@ -66,7 +66,10 @@ export async function buildTabularInspectorData(
row => row[`col-${colIndex}-${col.aggConfig.id}`].raw === value.raw
);
const filter = createFilter(aggConfigs, table, colIndex, rowIndex, value.raw);
queryFilter.addFilters(filter);

if (filter) {
queryFilter.addFilters(filter);
}
}),
filterOut:
isCellContentFilterable &&
Expand All @@ -75,14 +78,17 @@ export async function buildTabularInspectorData(
row => row[`col-${colIndex}-${col.aggConfig.id}`].raw === value.raw
);
const filter = createFilter(aggConfigs, table, colIndex, rowIndex, value.raw);
const notOther = value.raw !== '__other__';
const notMissing = value.raw !== '__missing__';
if (Array.isArray(filter)) {
filter.forEach(f => set(f, 'meta.negate', notOther && notMissing));
} else {
set(filter, 'meta.negate', notOther && notMissing);

if (filter) {
const notOther = value.raw !== '__other__';
const notMissing = value.raw !== '__missing__';
if (Array.isArray(filter)) {
filter.forEach(f => set(f, 'meta.negate', notOther && notMissing));
} else {
set(filter, 'meta.negate', notOther && notMissing);
}
queryFilter.addFilters(filter);
}
queryFilter.addFilters(filter);
}),
};
});
Expand Down
Loading

0 comments on commit 461eea4

Please sign in to comment.