Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -357,20 +357,77 @@ describe('Datatable Visualization', () => {
datasource.publicAPIMock.getTableSpec.mockReturnValue([{ columnId: 'c' }, { columnId: 'b' }]);
datasource.publicAPIMock.getOperationForColumnId.mockReturnValue({
dataType: 'string',
isBucketed: true,
isBucketed: false, // <= make them metrics
label: 'label',
});

const expression = datatableVisualization.toExpression(
{ layers: [layer] },
frame.datasourceLayers
) as Ast;

const tableArgs = buildExpression(expression).findFunction('lens_datatable_columns');

expect(tableArgs).toHaveLength(1);
expect(tableArgs[0].arguments).toEqual({
columnIds: ['c', 'b'],
});
});

it('returns no expression if the metric dimension is not defined', () => {
const datasource = createMockDatasource('test');
const layer = { layerId: 'a', columns: ['b', 'c'] };
const frame = mockFrame();
frame.datasourceLayers = { a: datasource.publicAPIMock };
datasource.publicAPIMock.getTableSpec.mockReturnValue([{ columnId: 'c' }, { columnId: 'b' }]);
datasource.publicAPIMock.getOperationForColumnId.mockReturnValue({
dataType: 'string',
isBucketed: true, // move it from the metric to the break down by side
label: 'label',
});

const expression = datatableVisualization.toExpression(
{ layers: [layer] },
frame.datasourceLayers
);

expect(expression).toEqual(null);
});
});

describe('#getErrorMessages', () => {
it('returns undefined if the datasource is missing a metric dimension', () => {
const datasource = createMockDatasource('test');
const layer = { layerId: 'a', columns: ['b', 'c'] };
const frame = mockFrame();
frame.datasourceLayers = { a: datasource.publicAPIMock };
datasource.publicAPIMock.getTableSpec.mockReturnValue([{ columnId: 'c' }, { columnId: 'b' }]);
datasource.publicAPIMock.getOperationForColumnId.mockReturnValue({
dataType: 'string',
isBucketed: true, // move it from the metric to the break down by side
label: 'label',
});

const error = datatableVisualization.getErrorMessages({ layers: [layer] }, frame);

expect(error).not.toBeDefined();
});

it('returns undefined if the metric dimension is defined', () => {
const datasource = createMockDatasource('test');
const layer = { layerId: 'a', columns: ['b', 'c'] };
const frame = mockFrame();
frame.datasourceLayers = { a: datasource.publicAPIMock };
datasource.publicAPIMock.getTableSpec.mockReturnValue([{ columnId: 'c' }, { columnId: 'b' }]);
datasource.publicAPIMock.getOperationForColumnId.mockReturnValue({
dataType: 'string',
isBucketed: false, // keep it a metric
label: 'label',
});

const error = datatableVisualization.getErrorMessages({ layers: [layer] }, frame);

expect(error).not.toBeDefined();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@

import { Ast } from '@kbn/interpreter/common';
import { i18n } from '@kbn/i18n';
import { SuggestionRequest, Visualization, VisualizationSuggestion, Operation } from '../types';
import {
SuggestionRequest,
Visualization,
VisualizationSuggestion,
Operation,
DatasourcePublicAPI,
} from '../types';
import { LensIconChartDatatable } from '../assets/chart_datatable';

export interface LayerState {
Expand Down Expand Up @@ -128,16 +134,13 @@ export const datatableVisualization: Visualization<DatatableVisualizationState>
},

getConfiguration({ state, frame, layerId }) {
const layer = state.layers.find((l) => l.layerId === layerId);
if (!layer) {
const { sortedColumns, datasource } =
getDataSourceAndSortedColumns(state, frame.datasourceLayers, layerId) || {};

if (!sortedColumns) {
return { groups: [] };
}

const datasource = frame.datasourceLayers[layer.layerId];
const originalOrder = datasource.getTableSpec().map(({ columnId }) => columnId);
// When we add a column it could be empty, and therefore have no order
const sortedColumns = Array.from(new Set(originalOrder.concat(layer.columns)));

return {
groups: [
{
Expand All @@ -146,7 +149,9 @@ export const datatableVisualization: Visualization<DatatableVisualizationState>
defaultMessage: 'Break down by',
}),
layerId: state.layers[0].layerId,
accessors: sortedColumns.filter((c) => datasource.getOperationForColumnId(c)?.isBucketed),
accessors: sortedColumns.filter(
(c) => datasource!.getOperationForColumnId(c)?.isBucketed
),
supportsMoreColumns: true,
filterOperations: (op) => op.isBucketed,
dataTestSubj: 'lnsDatatable_column',
Expand All @@ -158,7 +163,7 @@ export const datatableVisualization: Visualization<DatatableVisualizationState>
}),
layerId: state.layers[0].layerId,
accessors: sortedColumns.filter(
(c) => !datasource.getOperationForColumnId(c)?.isBucketed
(c) => !datasource!.getOperationForColumnId(c)?.isBucketed
),
supportsMoreColumns: true,
filterOperations: (op) => !op.isBucketed,
Expand Down Expand Up @@ -194,14 +199,19 @@ export const datatableVisualization: Visualization<DatatableVisualizationState>
};
},

toExpression(state, datasourceLayers, { title, description } = {}): Ast {
const layer = state.layers[0];
const datasource = datasourceLayers[layer.layerId];
const originalOrder = datasource.getTableSpec().map(({ columnId }) => columnId);
// When we add a column it could be empty, and therefore have no order
const sortedColumns = Array.from(new Set(originalOrder.concat(layer.columns)));
const operations = sortedColumns
.map((columnId) => ({ columnId, operation: datasource.getOperationForColumnId(columnId) }))
toExpression(state, datasourceLayers, { title, description } = {}): Ast | null {
const { sortedColumns, datasource } =
getDataSourceAndSortedColumns(state, datasourceLayers, state.layers[0].layerId) || {};

if (
sortedColumns?.length &&
sortedColumns.filter((c) => !datasource!.getOperationForColumnId(c)?.isBucketed).length === 0
) {
return null;
}

const operations = sortedColumns!
.map((columnId) => ({ columnId, operation: datasource!.getOperationForColumnId(columnId) }))
.filter((o): o is { columnId: string; operation: Operation } => !!o.operation);

return {
Expand Down Expand Up @@ -232,4 +242,24 @@ export const datatableVisualization: Visualization<DatatableVisualizationState>
],
};
},

getErrorMessages(state, frame) {
return undefined;
},
};

function getDataSourceAndSortedColumns(
state: DatatableVisualizationState,
datasourceLayers: Record<string, DatasourcePublicAPI>,
layerId: string
) {
const layer = state.layers.find((l: LayerState) => l.layerId === layerId);
if (!layer) {
return undefined;
}
const datasource = datasourceLayers[layer.layerId];
const originalOrder = datasource.getTableSpec().map(({ columnId }) => columnId);
// When we add a column it could be empty, and therefore have no order
const sortedColumns = Array.from(new Set(originalOrder.concat(layer.columns)));
return { datasource, sortedColumns };
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { SavedObjectReference } from 'kibana/public';
import { Ast } from '@kbn/interpreter/common';
import { Datasource, DatasourcePublicAPI, Visualization } from '../../types';
import { Datasource, DatasourcePublicAPI, FramePublicAPI, Visualization } from '../../types';
import { buildExpression } from './expression_helpers';
import { Document } from '../../persistence/saved_object_store';
import { VisualizeFieldContext } from '../../../../../../src/plugins/ui_actions/public';
Expand Down Expand Up @@ -91,3 +91,29 @@ export async function persistedStateToExpression(
datasourceLayers,
});
}

export const validateDatasourceAndVisualization = (
currentDataSource: Datasource | null,
currentDatasourceState: unknown | null,
currentVisualization: Visualization | null,
currentVisualizationState: unknown | undefined,
frameAPI: FramePublicAPI
):
| Array<{
shortMessage: string;
longMessage: string;
}>
| undefined => {
const datasourceValidationErrors = currentDatasourceState
? currentDataSource?.getErrorMessages(currentDatasourceState)
: undefined;

const visualizationValidationErrors = currentVisualizationState
? currentVisualization?.getErrorMessages(currentVisualizationState, frameAPI)
: undefined;

if (datasourceValidationErrors || visualizationValidationErrors) {
return [...(datasourceValidationErrors || []), ...(visualizationValidationErrors || [])];
}
return undefined;
};
Loading