Skip to content

Commit

Permalink
[Lens][TSVB] Navigate to Lens TSVB Table. (#143946)
Browse files Browse the repository at this point in the history
* Added convert to lens support for tsvb table

* Added unit tests

* Added functional tests

* Some refactoring of table metric option config

* Fixed imports

* Some small refactoring

* Fix flaky test

* Fixed test

* Some small fixes

* Fixed test
  • Loading branch information
VladLasitsa authored Oct 26, 2022
1 parent 21806ca commit c31c38c
Show file tree
Hide file tree
Showing 34 changed files with 1,019 additions and 148 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ export class SeriesEditor extends Component {
}
};

handleSeriesChange = (doc) => {
handleChange(this.props, doc);
};

render() {
const { limit, model, name, fields, colorPicker } = this.props;
const list = model[name].filter((val, index) => index < (limit || Infinity));
Expand All @@ -89,7 +93,7 @@ export class SeriesEditor extends Component {
disableDelete={model[name].length < 2}
fields={fields}
onAdd={() => handleAdd(this.props, newSeriesFn)}
onChange={(doc) => handleChange(this.props, doc)}
onChange={this.handleSeriesChange}
onClone={() => this.handleClone(row)}
onDelete={() => handleDelete(this.props, row)}
model={row}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { getDefaultQueryLanguage } from '../../lib/get_default_query_language';
import { checkIfNumericMetric } from '../../lib/check_if_numeric_metric';
import { QueryBarWrapper } from '../../query_bar_wrapper';
import { DATA_FORMATTERS } from '../../../../../common/enums';
import { DATA_FORMATTERS, BUCKET_TYPES } from '../../../../../common/enums';
import { isConfigurationFeatureEnabled } from '../../../../../common/check_ui_restrictions';
import { filterCannotBeAppliedErrorMessage } from '../../../../../common/errors';
import { tsvbEditorRowStyles } from '../../../styles/common.styles';
Expand All @@ -50,13 +50,20 @@ class TableSeriesConfigUi extends Component {
}
}

handleAggregateByChange = (selectedOptions) => {
this.props.onChange({
aggregate_by: selectedOptions?.[0],
});
};

handleSelectChange = createSelectHandler(this.props.onChange);
handleTextChange = createTextHandler(this.props.onChange);

changeModelFormatter = (formatter) => this.props.onChange({ formatter });

render() {
const defaults = { offset_time: '', value_template: '{{value}}' };
const model = { ...defaults, ...this.props.model };
const handleSelectChange = createSelectHandler(this.props.onChange);
const handleTextChange = createTextHandler(this.props.onChange);
const htmlId = htmlIdGenerator();

const functionOptions = [
Expand Down Expand Up @@ -160,7 +167,7 @@ class TableSeriesConfigUi extends Component {
fullWidth
>
<EuiFieldText
onChange={handleTextChange('value_template')}
onChange={this.handleTextChange('value_template')}
value={model.value_template}
disabled={model.formatter === DATA_FORMATTERS.DEFAULT}
fullWidth
Expand Down Expand Up @@ -214,19 +221,15 @@ class TableSeriesConfigUi extends Component {
<EuiHorizontalRule margin="s" />

<EuiFlexGroup responsive={false} wrap={true}>
<EuiFlexItem grow={true}>
<EuiFlexItem grow={true} data-test-subj="tsvbAggregateBySelect">
<FieldSelect
label={
<FormattedMessage id="visTypeTimeseries.table.fieldLabel" defaultMessage="Field" />
}
fields={this.props.fields}
indexPattern={this.props.panel.index_pattern}
value={model.aggregate_by}
onChange={(value) =>
this.props.onChange({
aggregate_by: value?.[0],
})
}
onChange={this.handleAggregateByChange}
fullWidth
restrict={[
KBN_FIELD_TYPES.NUMBER,
Expand All @@ -236,7 +239,7 @@ class TableSeriesConfigUi extends Component {
KBN_FIELD_TYPES.STRING,
]}
uiRestrictions={this.props.uiRestrictions}
type={'terms'}
type={BUCKET_TYPES.TERMS}
/>
</EuiFlexItem>
<EuiFlexItem grow={true}>
Expand All @@ -251,9 +254,10 @@ class TableSeriesConfigUi extends Component {
fullWidth
>
<EuiComboBox
data-test-subj="tsvbAggregateFunctionCombobox"
options={functionOptions}
selectedOptions={selectedAggFuncOption ? [selectedAggFuncOption] : []}
onChange={handleSelectChange('aggregate_function')}
onChange={this.handleSelectChange('aggregate_function')}
singleSelection={{ asPlainText: true }}
fullWidth
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
* Side Public License, v 1.
*/

import { Vis } from '@kbn/visualizations-plugin/public';
import { METRIC_TYPES } from '@kbn/data-plugin/public';
import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub';
import { TSVB_METRIC_TYPES } from '../../../common/enums';
import { Metric } from '../../../common/types';
import { Panel, Metric } from '../../../common/types';
import { convertToLens } from '.';
import { createPanel, createSeries } from '../lib/__mocks__';
import { AvgColumn } from '../lib/convert';
Expand Down Expand Up @@ -58,6 +59,10 @@ describe('convertToLens', () => {
series: [createSeries({ metrics: [metric] })],
});

const vis = {
params: model,
} as Vis<Panel>;

const metricColumn: AvgColumn = {
columnId: 'col-id',
dataType: 'number',
Expand Down Expand Up @@ -89,51 +94,55 @@ describe('convertToLens', () => {

test('should return null for invalid metrics', async () => {
mockIsValidMetrics.mockReturnValue(null);
const result = await convertToLens(model);
const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockIsValidMetrics).toBeCalledTimes(1);
});

test('should return null for invalid or unsupported metrics', async () => {
mockGetMetricsColumns.mockReturnValue(null);
const result = await convertToLens(model);
const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockGetMetricsColumns).toBeCalledTimes(1);
});

test('should return null for invalid or unsupported buckets', async () => {
mockGetBucketsColumns.mockReturnValue(null);
const result = await convertToLens(model);
const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockGetBucketsColumns).toBeCalledTimes(1);
});

test('should return null if metric is staticValue', async () => {
const result = await convertToLens({
...model,
series: [
{
...model.series[0],
metrics: [...model.series[0].metrics, { type: TSVB_METRIC_TYPES.STATIC } as Metric],
},
],
});
params: {
...model,
series: [
{
...model.series[0],
metrics: [...model.series[0].metrics, { type: TSVB_METRIC_TYPES.STATIC } as Metric],
},
],
},
} as Vis<Panel>);
expect(result).toBeNull();
expect(mockGetDataSourceInfo).toBeCalledTimes(0);
});

test('should return null if only series agg is specified', async () => {
const result = await convertToLens({
...model,
series: [
{
...model.series[0],
metrics: [
{ type: TSVB_METRIC_TYPES.SERIES_AGG, function: 'min', id: 'some-id' } as Metric,
],
},
],
});
params: {
...model,
series: [
{
...model.series[0],
metrics: [
{ type: TSVB_METRIC_TYPES.SERIES_AGG, function: 'min', id: 'some-id' } as Metric,
],
},
],
},
} as Vis<Panel>);
expect(result).toBeNull();
});

Expand All @@ -142,7 +151,7 @@ describe('convertToLens', () => {
mockGetSeriesAgg.mockReturnValue({ metrics: [metric] });
mockGetConfigurationForGauge.mockReturnValue(null);

const result = await convertToLens(model);
const result = await convertToLens(vis);
expect(result).toBeNull();
});

Expand All @@ -151,8 +160,8 @@ describe('convertToLens', () => {
mockGetSeriesAgg.mockReturnValue({ metrics: [metric] });
mockGetConfigurationForGauge.mockReturnValue({});

const result = await convertToLens(
createPanel({
const result = await convertToLens({
params: createPanel({
series: [
createSeries({
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
Expand All @@ -163,8 +172,8 @@ describe('convertToLens', () => {
hidden: false,
}),
],
})
);
}),
} as Vis<Panel>);
expect(result).toBeDefined();
expect(result?.type).toBe('lnsMetric');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ const getMaxFormula = (metric: Metric, column?: Column) => {
}))`;
};

export const convertToLens: ConvertTsvbToLensVisualization = async (model, timeRange) => {
export const convertToLens: ConvertTsvbToLensVisualization = async (
{ params: model },
timeRange
) => {
const dataViews = getDataViewsStart();

const series = model.series[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Side Public License, v 1.
*/

import { Vis } from '@kbn/visualizations-plugin/public';
import type { Panel } from '../../common/types';
import { convertTSVBtoLensConfiguration } from '.';

Expand Down Expand Up @@ -42,7 +43,9 @@ describe('convertTSVBtoLensConfiguration', () => {
...model,
type: 'markdown',
} as Panel;
const triggerOptions = await convertTSVBtoLensConfiguration(metricModel);
const triggerOptions = await convertTSVBtoLensConfiguration({
params: metricModel,
} as Vis<Panel>);
expect(triggerOptions).toBeNull();
});

Expand All @@ -51,7 +54,9 @@ describe('convertTSVBtoLensConfiguration', () => {
...model,
use_kibana_indexes: false,
};
const triggerOptions = await convertTSVBtoLensConfiguration(stringIndexPatternModel);
const triggerOptions = await convertTSVBtoLensConfiguration({
params: stringIndexPatternModel,
} as Vis<Panel>);
expect(triggerOptions).toBeNull();
});
});
15 changes: 10 additions & 5 deletions src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Side Public License, v 1.
*/

import { Vis } from '@kbn/visualizations-plugin/public';
import { TimeRange } from '@kbn/data-plugin/common';
import type { Panel } from '../../common/types';
import { PANEL_TYPES } from '../../common/enums';
Expand All @@ -29,6 +30,10 @@ const getConvertFnByType = (type: PANEL_TYPES) => {
const { convertToLens } = await import('./gauge');
return convertToLens;
},
[PANEL_TYPES.TABLE]: async () => {
const { convertToLens } = await import('./table');
return convertToLens;
},
};

return convertionFns[type]?.();
Expand All @@ -39,17 +44,17 @@ const getConvertFnByType = (type: PANEL_TYPES) => {
* Returns the Lens model, only if it is supported. If not, it returns null.
* In case of null, the menu item is disabled and the user can't navigate to Lens.
*/
export const convertTSVBtoLensConfiguration = async (model: Panel, timeRange?: TimeRange) => {
export const convertTSVBtoLensConfiguration = async (vis: Vis<Panel>, timeRange?: TimeRange) => {
// Disables the option for not supported charts, for the string mode and for series with annotations
if (!model.use_kibana_indexes) {
if (!vis.params.use_kibana_indexes) {
return null;
}
// Disables if model is invalid
if (model.isModelInvalid) {
if (vis.params.isModelInvalid) {
return null;
}

const convertFn = await getConvertFnByType(model.type);
const convertFn = await getConvertFnByType(vis.params.type);

return (await convertFn?.(model, timeRange)) ?? null;
return (await convertFn?.(vis, timeRange)) ?? null;
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { getConfigurationForMetric, getConfigurationForGauge } from '.';

const mockGetPalette = jest.fn();

jest.mock('./palette', () => ({
jest.mock('../palette', () => ({
getPalette: jest.fn(() => mockGetPalette()),
}));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import color from 'color';
import { MetricVisConfiguration } from '@kbn/visualizations-plugin/common';
import { Panel } from '../../../../../common/types';
import { Column, Layer } from '../../convert';
import { getPalette } from './palette';
import { getPalette } from '../palette';
import { findMetricColumn, getMetricWithCollapseFn } from '../../../utils';

export const getConfigurationForMetric = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

import { getPalette } from './palette';
import { getPalette } from '.';

describe('getPalette', () => {
const baseColor = '#fff';
Expand Down
Loading

0 comments on commit c31c38c

Please sign in to comment.