Skip to content

Commit

Permalink
[Lens] Prevent editor crash on histograms datatype mix (#98453)
Browse files Browse the repository at this point in the history
  • Loading branch information
dej611 authored Apr 27, 2021
1 parent 61d5737 commit 088212b
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 2 deletions.
54 changes: 54 additions & 0 deletions x-pack/plugins/lens/public/xy_visualization/visualization.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,60 @@ describe('xy_visualization', () => {
},
]);
});

it('should return an error if two incompatible xAccessors (multiple layers) are used', () => {
// current incompatibility is only for date and numeric histograms as xAccessors
const datasourceLayers = {
first: mockDatasource.publicAPIMock,
second: createMockDatasource('testDatasource').publicAPIMock,
};
datasourceLayers.first.getOperationForColumnId = jest.fn((id: string) =>
id === 'a'
? (({
dataType: 'date',
scale: 'interval',
} as unknown) as Operation)
: null
);
datasourceLayers.second.getOperationForColumnId = jest.fn((id: string) =>
id === 'e'
? (({
dataType: 'number',
scale: 'interval',
} as unknown) as Operation)
: null
);
expect(
xyVisualization.getErrorMessages(
{
...exampleState(),
layers: [
{
layerId: 'first',
seriesType: 'area',
splitAccessor: 'd',
xAccessor: 'a',
accessors: ['b'],
},
{
layerId: 'second',
seriesType: 'area',
splitAccessor: 'd',
xAccessor: 'e',
accessors: ['b'],
},
],
},
datasourceLayers
)
).toEqual([
{
shortMessage: 'Wrong data type for Horizontal axis.',
longMessage:
'Data type mismatch for the Horizontal axis. Cannot mix date and number interval types.',
},
]);
});
});

describe('#getWarningMessages', () => {
Expand Down
57 changes: 55 additions & 2 deletions x-pack/plugins/lens/public/xy_visualization/visualization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,14 @@ import { PaletteRegistry } from 'src/plugins/charts/public';
import { DataPublicPluginStart } from 'src/plugins/data/public';
import { getSuggestions } from './xy_suggestions';
import { LayerContextMenu, XyToolbar, DimensionEditor } from './xy_config_panel';
import { Visualization, OperationMetadata, VisualizationType, AccessorConfig } from '../types';
import { State, SeriesType, visualizationTypes, XYLayerConfig } from './types';
import {
Visualization,
OperationMetadata,
VisualizationType,
AccessorConfig,
DatasourcePublicAPI,
} from '../types';
import { State, SeriesType, visualizationTypes, XYLayerConfig, XYState } from './types';
import { isHorizontalChart } from './state_helpers';
import { toExpression, toPreviewExpression, getSortedAccessors } from './to_expression';
import { LensIconChartBarStacked } from '../assets/chart_bar_stacked';
Expand Down Expand Up @@ -374,6 +380,9 @@ export const getXyVisualization = ({
}

if (datasourceLayers && state) {
// temporary fix for #87068
errors.push(...checkXAccessorCompatibility(state, datasourceLayers));

for (const layer of state.layers) {
const datasourceAPI = datasourceLayers[layer.layerId];
if (datasourceAPI) {
Expand Down Expand Up @@ -517,3 +526,47 @@ function newLayerState(seriesType: SeriesType, layerId: string): XYLayerConfig {
accessors: [],
};
}

// min requirement for the bug:
// * 2 or more layers
// * at least one with date histogram
// * at least one with interval function
function checkXAccessorCompatibility(
state: XYState,
datasourceLayers: Record<string, DatasourcePublicAPI>
) {
const errors = [];
const hasDateHistogramSet = state.layers.some(checkIntervalOperation('date', datasourceLayers));
const hasNumberHistogram = state.layers.some(checkIntervalOperation('number', datasourceLayers));
if (state.layers.length > 1 && hasDateHistogramSet && hasNumberHistogram) {
errors.push({
shortMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXShort', {
defaultMessage: `Wrong data type for {axis}.`,
values: {
axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }),
},
}),
longMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXLong', {
defaultMessage: `Data type mismatch for the {axis}. Cannot mix date and number interval types.`,
values: {
axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }),
},
}),
});
}
return errors;
}

function checkIntervalOperation(
dataType: 'date' | 'number',
datasourceLayers: Record<string, DatasourcePublicAPI>
) {
return (layer: XYLayerConfig) => {
const datasourceAPI = datasourceLayers[layer.layerId];
if (!layer.xAccessor) {
return false;
}
const operation = datasourceAPI?.getOperationForColumnId(layer.xAccessor);
return Boolean(operation?.dataType === dataType && operation.scale === 'interval');
};
}

0 comments on commit 088212b

Please sign in to comment.