Skip to content

Commit

Permalink
[TSVB] Apply filter event is not working for formatted fields (#128228)
Browse files Browse the repository at this point in the history
* [WIP][TSVB] Apply filter event is not working for formatted fields

* Add termsSplitValue that might be as single string for terms as MultiFieldKey for multi terms, that allows to create filters for grouped by multi terms data

* Update tests and fix some nits

* Add couple of functional tests

* Remove flaky test

* Update test to fix flakiness

* Fix some nits

* Shorten a condition

* Fix import

* Wrap setting combo box values into retry.try to get rid of flakiness

* In functional test increase sleep a bit to make sure group by field has appeared

* Refactor some code

* Try again to update test coordinates

* Update test coordinates

* Fix condition for terms value

* Fix export of MultiFieldKey

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
DianaDerevyankina and kibanamachine authored Apr 7, 2022
1 parent c3798e7 commit 3dc61c8
Show file tree
Hide file tree
Showing 12 changed files with 112 additions and 20 deletions.
1 change: 1 addition & 0 deletions src/plugins/data/common/search/aggs/buckets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export * from './significant_text_fn';
export * from './significant_text';
export * from './terms_fn';
export * from './terms';
export { MultiFieldKey } from './multi_field_key';
export * from './multi_terms_fn';
export * from './multi_terms';
export * from './rare_terms_fn';
Expand Down
1 change: 1 addition & 0 deletions src/plugins/vis_types/timeseries/common/types/vis_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export interface PanelData {
seriesId: string;
splitByLabel: string;
isSplitByTerms: boolean;
termsSplitKey?: string | string[];
error?: string;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export function FieldSelectItem({
singleSelection={{ asPlainText: true }}
isInvalid={isInvalid}
fullWidth={true}
data-test-subj="fieldSelectItem"
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
Expand All @@ -69,6 +70,7 @@ export function FieldSelectItem({
disableDelete={disableDelete}
disableAdd={disableAdd}
responsive={false}
testSubj="fieldSelectItem"
/>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,14 +200,16 @@ describe('convert series to datatables', () => {
expect(Object.keys(tables).sort()).toEqual([model.series[0].id].sort());

expect(tables.series1.rows.length).toEqual(8);
const expected1 = series[0].data.map((d) => {
d.push(parseInt([series[0].label].flat()[0], 10));
return d;
});
const expected2 = series[1].data.map((d) => {
d.push(parseInt([series[1].label].flat()[0], 10));
return d;
});
const expected1 = series[0].data.map((d) => ({
'0': d[0],
'1': d[1],
'2': parseInt([series[0].label].flat()[0], 10),
}));
const expected2 = series[1].data.map((d) => ({
'0': d[0],
'1': d[1],
'2': parseInt([series[1].label].flat()[0], 10),
}));
expect(tables.series1.rows).toEqual([...expected1, ...expected2]);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import { Query } from 'src/plugins/data/common';
import { TimeseriesVisParams } from '../../../types';
import type { PanelData, Metric } from '../../../../common/types';
import { getMultiFieldLabel, getFieldsForTerms } from '../../../../common/fields_utils';
import {
BUCKET_TYPES as DATA_PLUGIN_BUCKET_TYPES,
MultiFieldKey,
} from '../../../../../../data/common';
import { BUCKET_TYPES, TSVB_METRIC_TYPES } from '../../../../common/enums';
import { fetchIndexPattern } from '../../../../common/index_patterns_utils';
import { getDataStart, getDataViewsStart } from '../../../services';
Expand All @@ -26,6 +30,7 @@ interface FilterParams {
interface TSVBColumns {
id: number;
name: string;
fields?: string[];
isMetric: boolean;
type: string;
params?: FilterParams[];
Expand All @@ -42,7 +47,12 @@ export const addMetaToColumns = (
let params: unknown = {
field: field?.spec.name,
};
if (column.type === BUCKET_TYPES.FILTERS && column.params) {
if (column.type === DATA_PLUGIN_BUCKET_TYPES.MULTI_TERMS) {
params = {
fields: column.fields,
otherBucket: true,
};
} else if (column.type === BUCKET_TYPES.FILTERS && column.params) {
const filters = column.params.map((col) => ({
input: col.filter,
label: col.label,
Expand Down Expand Up @@ -129,12 +139,15 @@ export const convertSeriesToDataTable = async (

// Adds an extra column, if the layer is split by terms or filters aggregation
if (isGroupedByTerms) {
const fieldsForTerms = getFieldsForTerms(layer.terms_field);
id++;
columns.push({
id,
name: getMultiFieldLabel(getFieldsForTerms(layer.terms_field)),
name: getMultiFieldLabel(fieldsForTerms),
fields: fieldsForTerms,
isMetric: false,
type: BUCKET_TYPES.TERMS,
type:
fieldsForTerms.length > 1 ? DATA_PLUGIN_BUCKET_TYPES.MULTI_TERMS : BUCKET_TYPES.TERMS,
});
} else if (isGroupedByFilters) {
id++;
Expand All @@ -151,15 +164,24 @@ export const convertSeriesToDataTable = async (
const filtersColumn = columns.find((col) => col.type === BUCKET_TYPES.FILTERS);
let rows: DatatableRow[] = [];
for (let j = 0; j < seriesPerLayer.length; j++) {
const data = seriesPerLayer[j].data.map((rowData) => {
const row: DatatableRow = [rowData[0], rowData[1]];
// If the layer is split by terms aggregation, the data array should also contain the split value.
const { data, label, isSplitByTerms, termsSplitKey } = seriesPerLayer[j];
const seriesData = data.map((rowData) => {
let rowId = X_ACCESSOR_INDEX;
const rowsData = { [rowId++]: rowData[0], [rowId++]: rowData[1] };

let splitValue;
if (isGroupedByTerms || filtersColumn) {
row.push([seriesPerLayer[j].label].flat()[0]);
const termsValue = Array.isArray(termsSplitKey)
? new MultiFieldKey({ key: termsSplitKey })
: termsSplitKey;
splitValue = {
[rowId]: isSplitByTerms && termsValue !== undefined ? termsValue : [label].flat()[0],
};
}
return row;

return splitValue ? { ...rowsData, ...splitValue } : rowsData;
});
rows = [...rows, ...data];
rows = [...rows, ...seriesData];
}
tables[layer.id] = {
type: 'datatable',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ export const getClickFilterData = (
const index = table.rows.findIndex((row) => {
const condition =
geometry.x === row[X_ACCESSOR_INDEX] && geometry.y === row[X_ACCESSOR_INDEX + 1];
return splitLabel ? condition && row[X_ACCESSOR_INDEX + 2].toString() === label : condition;
if (splitLabel) {
const value =
row[X_ACCESSOR_INDEX + 2].keys?.join() ?? row[X_ACCESSOR_INDEX + 2].toString();
return condition && value === label;
}
return condition;
});
if (index < 0) return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export const TimeseriesConfig = injectI18n(function (props) {
selectedOptions={selectedStackedOption ? [selectedStackedOption] : []}
onChange={handleSelectChange('stacked')}
singleSelection={{ asPlainText: true }}
data-test-subj="seriesStackedComboBox"
/>
</EuiFormRow>
</EuiFlexItem>
Expand Down Expand Up @@ -285,6 +286,7 @@ export const TimeseriesConfig = injectI18n(function (props) {
selectedOptions={selectedStackedOption ? [selectedStackedOption] : []}
onChange={handleSelectChange('stacked')}
singleSelection={{ asPlainText: true }}
data-test-subj="seriesStackedComboBox"
/>
</EuiFormRow>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ describe('getSplits(resp, panel, series)', () => {
meta: { bucketSize: 10 },
color: 'rgb(255, 0, 0)',
splitByLabel: 'Overall Average of Average of cpu',
termsSplitKey: 'example-01',
timeseries: { buckets: [] },
SIBAGG: { value: 1 },
},
Expand All @@ -97,6 +98,7 @@ describe('getSplits(resp, panel, series)', () => {
meta: { bucketSize: 10 },
color: 'rgb(255, 0, 0)',
splitByLabel: 'Overall Average of Average of cpu',
termsSplitKey: 'example-02',
timeseries: { buckets: [] },
SIBAGG: { value: 2 },
},
Expand Down Expand Up @@ -145,6 +147,7 @@ describe('getSplits(resp, panel, series)', () => {
meta: { bucketSize: 10 },
color: 'rgb(255, 0, 0)',
splitByLabel: 'Overall Average of Average of cpu',
termsSplitKey: 'example-01',
timeseries: { buckets: [] },
SIBAGG: { value: 1 },
},
Expand All @@ -156,6 +159,7 @@ describe('getSplits(resp, panel, series)', () => {
meta: { bucketSize: 10 },
color: 'rgb(255, 0, 0)',
splitByLabel: 'Overall Average of Average of cpu',
termsSplitKey: 'example-02',
timeseries: { buckets: [] },
SIBAGG: { value: 2 },
},
Expand Down Expand Up @@ -208,6 +212,7 @@ describe('getSplits(resp, panel, series)', () => {
meta: { bucketSize: 10 },
color: 'rgb(255, 0, 0)',
splitByLabel: 'Overall Average of Average of cpu',
termsSplitKey: 'example-01',
timeseries: { buckets: [] },
SIBAGG: { value: 1 },
},
Expand All @@ -220,6 +225,7 @@ describe('getSplits(resp, panel, series)', () => {
meta: { bucketSize: 10 },
color: 'rgb(255, 0, 0)',
splitByLabel: 'Overall Average of Average of cpu',
termsSplitKey: 'example-02',
timeseries: { buckets: [] },
SIBAGG: { value: 2 },
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ interface SplittedData<TMeta extends BaseMeta = BaseMeta> {
id: string;
splitByLabel: string;
label: string;
labelFormatted?: string;
termsSplitKey?: string | string[];
color: string;
meta: TMeta;
timeseries: {
Expand Down Expand Up @@ -73,6 +75,7 @@ export async function getSplits<TRawResponse = unknown, TMeta extends BaseMeta =
bucket.labelFormatted = bucket.key_as_string ? formatKey(bucket.key_as_string, series) : '';
bucket.color = color.string();
bucket.meta = meta;
bucket.termsSplitKey = bucket.key;
return bucket;
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export function stdMetric(resp, panel, series, meta, extractFields) {
label: split.label,
splitByLabel: split.splitByLabel,
labelFormatted: split.labelFormatted,
termsSplitKey: split.termsSplitKey,
color: split.color,
data,
...decoration,
Expand Down
29 changes: 29 additions & 0 deletions test/functional/apps/visualize/_tsvb_time_series.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,35 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const hasMachineRawFilter = await filterBar.hasFilter('machine.os.raw', 'win 7');
expect(hasMachineRawFilter).to.be(true);
});

it('should create a filter for series with multiple split by terms fields one of which has formatting', async () => {
const expectedFilterPills = ['0, win xp, logstash-2015.09.20'];
await visualBuilder.setMetricsGroupByTerms('bytes');
await visualBuilder.setAnotherGroupByTermsField('machine.os.raw');
await visualBuilder.setAnotherGroupByTermsField('_index');

await visualBuilder.clickSeriesOption();
await visualBuilder.setChartType('Bar');
await visualBuilder.setStackedType('Stacked');
await visualBuilder.clickPanelOptions('timeSeries');
await visualBuilder.setIntervalValue('1w');

const el = await elasticChart.getCanvas();
await el.scrollIntoViewIfNecessary();
await browser
.getActions()
.move({ x: 100, y: 65, origin: el._webElement })
.click()
.perform();

await retry.try(async () => {
await testSubjects.click('applyFiltersPopoverButton');
await testSubjects.missingOrFail('applyFiltersPopoverButton');
});

const filterPills = await filterBar.getFiltersLabel();
expect(filterPills).to.eql(expectedFilterPills);
});
});
});

Expand Down
22 changes: 20 additions & 2 deletions test/functional/page_objects/visual_builder_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -782,11 +782,24 @@ export class VisualBuilderPageObject extends FtrService {
await this.setMetricsGroupBy('terms');
await this.common.sleep(1000);
const byField = await this.testSubjects.find('groupByField');
await this.comboBox.setElement(byField, field);
await this.retry.try(async () => {
await this.comboBox.setElement(byField, field);
});

await this.setMetricsGroupByFiltering(filtering.include, filtering.exclude);
}

public async setAnotherGroupByTermsField(field: string) {
const fieldSelectAddButtons = await this.testSubjects.findAll('fieldSelectItemAddBtn');
await fieldSelectAddButtons[fieldSelectAddButtons.length - 1].click();
await this.common.sleep(2000);
const byFields = await this.testSubjects.findAll('fieldSelectItem');
const selectedByField = byFields[byFields.length - 1];
await this.retry.try(async () => {
await this.comboBox.setElement(selectedByField, field);
});
}

public async setMetricsGroupByFiltering(include?: string, exclude?: string) {
const setFilterValue = async (value: string | undefined, subjectKey: string) => {
if (typeof value === 'string') {
Expand Down Expand Up @@ -821,11 +834,16 @@ export class VisualBuilderPageObject extends FtrService {
await filterLabelInput[nth].type(label);
}

public async setChartType(type: string, nth: number = 0) {
public async setChartType(type: 'Bar' | 'Line', nth: number = 0) {
const seriesChartTypeComboBoxes = await this.testSubjects.findAll('seriesChartTypeComboBox');
return await this.comboBox.setElement(seriesChartTypeComboBoxes[nth], type);
}

public async setStackedType(stackedType: string, nth: number = 0) {
const seriesChartTypeComboBoxes = await this.testSubjects.findAll('seriesStackedComboBox');
return await this.comboBox.setElement(seriesChartTypeComboBoxes[nth], stackedType);
}

public async setSeriesFilter(query: string) {
const seriesFilterQueryInput = await this.testSubjects.find('seriesConfigQueryBar');
await seriesFilterQueryInput.type(query);
Expand Down

0 comments on commit 3dc61c8

Please sign in to comment.