Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Lens] Extend explore data in Discover/open in Discover drilldown to visualizations with annotations and reference lines #147541

Merged
merged 5 commits into from Dec 15, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx
Expand Up @@ -479,9 +479,14 @@ export const LensTopNavMenu = ({
if (!activeDatasourceId || !discoverLocator) {
return;
}
if (visualization.activeId == null) {
return;
}
return getLayerMetaInfo(
datasourceMap[activeDatasourceId],
datasourceStates[activeDatasourceId].state,
visualizationMap[visualization.activeId],
visualization.state,
activeData,
dataViews.indexPatterns,
data.query.timefilter.timefilter.getTime(),
Expand All @@ -490,8 +495,10 @@ export const LensTopNavMenu = ({
}, [
activeDatasourceId,
discoverLocator,
visualization,
datasourceMap,
datasourceStates,
visualizationMap,
activeData,
dataViews.indexPatterns,
data.query.timefilter.timefilter,
Expand Down
197 changes: 136 additions & 61 deletions x-pack/plugins/lens/public/app_plugin/show_underlying_data.test.ts
Expand Up @@ -5,11 +5,11 @@
* 2.0.
*/

import { createMockDatasource } from '../mocks';
import { createMockDatasource, createMockVisualization } from '../mocks';
import { combineQueryAndFilters, getLayerMetaInfo } from './show_underlying_data';
import { Filter } from '@kbn/es-query';
import { DatasourcePublicAPI } from '../types';
import { createMockedIndexPattern } from '../datasources/form_based/mocks';
import { LayerType } from '..';

describe('getLayerMetaInfo', () => {
const capabilities = {
Expand All @@ -21,6 +21,8 @@ describe('getLayerMetaInfo', () => {
getLayerMetaInfo(
createMockDatasource('testDatasource'),
{},
createMockVisualization('testVisualization'),
{},
undefined,
{},
undefined,
Expand All @@ -29,14 +31,17 @@ describe('getLayerMetaInfo', () => {
).toBe('Visualization has no data available to show');
});

it('should return error in case of multiple layers', () => {
it('should return error in case of multiple data layers', () => {
const mockDatasource = createMockDatasource('testDatasource');
mockDatasource.getLayers.mockReturnValue(['layer1', 'layer2']);
expect(
getLayerMetaInfo(
createMockDatasource('testDatasource'),
mockDatasource,
{},
createMockVisualization('testVisualization'),
{},
{
datatable1: { type: 'datatable', columns: [], rows: [] },
datatable2: { type: 'datatable', columns: [], rows: [] },
},
{},
undefined,
Expand All @@ -46,28 +51,118 @@ describe('getLayerMetaInfo', () => {
});

it('should return error in case of missing activeDatasource', () => {
expect(getLayerMetaInfo(undefined, {}, undefined, {}, undefined, capabilities).error).toBe(
'Visualization has no data available to show'
);
expect(
getLayerMetaInfo(
undefined,
{},
createMockVisualization('testVisualization'),
{},
undefined,
{},
undefined,
capabilities
).error
).toBe('Visualization has no data available to show');
});

it('should return error in case of missing datasource configuration/state', () => {
expect(
getLayerMetaInfo(
createMockDatasource('testDatasource'),
undefined,
createMockVisualization('testVisualization'),
{},
{},
{},
undefined,
capabilities
).error
).toBe('Visualization has no data available to show');
});

it('should return error in case of missing configuration/state', () => {
it('should return error in case of missing activeVisualization', () => {
expect(
getLayerMetaInfo(
createMockDatasource('testDatasource'),
{},
undefined,
{},
undefined,
{},
undefined,
capabilities
).error
).toBe('Visualization has no data available to show');
});

it('should return error in case of missing visualization configuration/state', () => {
expect(
getLayerMetaInfo(
createMockDatasource('testDatasource'),
{},
createMockVisualization('testVisualization'),
undefined,
{},
{},
undefined,
capabilities
).error
).toBe('Visualization has no data available to show');
});

it('should ignore the number of datatables passed, rather check the datasource and visualization configuration', () => {
expect(
getLayerMetaInfo(
createMockDatasource('testDatasource', {
getFilters: jest.fn(() => ({
enabled: { kuery: [], lucene: [] },
disabled: { kuery: [], lucene: [] },
})),
}),
{},
createMockVisualization('testVisualization'),
{},
{
datatable1: { type: 'datatable', columns: [], rows: [] },
datatable2: { type: 'datatable', columns: [], rows: [] },
},
{},
undefined,
capabilities
).error
).toBeUndefined();
});

it('should return no multiple layers error when non-data layers are used together with a single data layer', () => {
const mockDatasource = createMockDatasource('testDatasource', {
getFilters: jest.fn(() => ({
enabled: { kuery: [], lucene: [] },
disabled: { kuery: [], lucene: [] },
})),
});
mockDatasource.getLayers.mockReturnValue(['layer1', 'layer2', 'layer3']);
const mockVisualization = createMockVisualization('testVisualization');
let counter = 0;
const layerTypes: LayerType[] = ['data', 'annotations', 'referenceLine'];
mockVisualization.getLayerType.mockImplementation(() => layerTypes[counter++]);
expect(
getLayerMetaInfo(
mockDatasource,
{},
mockVisualization,
{},
{
datatable1: { type: 'datatable', columns: [], rows: [] },
},
{},
undefined,
capabilities
).error
).toBeUndefined();
});

it('should return error in case of a timeshift declared in a column', () => {
const mockDatasource = createMockDatasource('testDatasource');
const updatedPublicAPI: DatasourcePublicAPI = {
datasourceId: 'testDatasource',
const mockDatasource = createMockDatasource('testDatasource', {
getOperationForColumnId: jest.fn(() => ({
dataType: 'number',
isBucketed: false,
Expand All @@ -78,39 +173,33 @@ describe('getLayerMetaInfo', () => {
hasTimeShift: true,
hasReducedTimeRange: true,
})),
getTableSpec: jest.fn(),
getVisualDefaults: jest.fn(),
getSourceId: jest.fn(),
getMaxPossibleNumValues: jest.fn(),
getFilters: jest.fn(),
isTextBasedLanguage: jest.fn(() => false),
hasDefaultTimeField: jest.fn(() => true),
};
mockDatasource.getPublicAPI.mockReturnValue(updatedPublicAPI);
});
expect(
getLayerMetaInfo(createMockDatasource('testDatasource'), {}, {}, {}, undefined, capabilities)
.error
getLayerMetaInfo(
mockDatasource,
{},
createMockVisualization('testVisualization'),
{},
{},
{},
undefined,
capabilities
).error
).toBe('Visualization has no data available to show');
});

it('should return error in case of getFilters returning errors', () => {
const mockDatasource = createMockDatasource('testDatasource');
const updatedPublicAPI: DatasourcePublicAPI = {
const mockDatasource = createMockDatasource('testDatasource', {
datasourceId: 'formBased',
getOperationForColumnId: jest.fn(),
getTableSpec: jest.fn(() => [{ columnId: 'col1', fields: ['bytes'] }]),
getVisualDefaults: jest.fn(),
getSourceId: jest.fn(),
getMaxPossibleNumValues: jest.fn(),
getFilters: jest.fn(() => ({ error: 'filters error' })),
isTextBasedLanguage: jest.fn(() => false),
hasDefaultTimeField: jest.fn(() => true),
};
mockDatasource.getPublicAPI.mockReturnValue(updatedPublicAPI);
});
expect(
getLayerMetaInfo(
mockDatasource,
{}, // the publicAPI has been mocked, so no need for a state here
createMockVisualization('testVisualization'),
{},
{
datatable1: { type: 'datatable', columns: [], rows: [] },
},
Expand All @@ -122,24 +211,18 @@ describe('getLayerMetaInfo', () => {
});

it('should not be visible if discover is not available', () => {
const mockDatasource = createMockDatasource('testDatasource');
const updatedPublicAPI: DatasourcePublicAPI = {
const mockDatasource = createMockDatasource('testDatasource', {
datasourceId: 'indexpattern',
getOperationForColumnId: jest.fn(),
getTableSpec: jest.fn(() => [{ columnId: 'col1', fields: ['bytes'] }]),
getVisualDefaults: jest.fn(),
getSourceId: jest.fn(),
getMaxPossibleNumValues: jest.fn(),
getFilters: jest.fn(() => ({ error: 'filters error' })),
isTextBasedLanguage: jest.fn(() => false),
hasDefaultTimeField: jest.fn(() => true),
};
mockDatasource.getPublicAPI.mockReturnValue(updatedPublicAPI);
});
// both capabilities should be enabled to enable discover
expect(
getLayerMetaInfo(
mockDatasource,
{},
createMockVisualization('testVisualization'),
{},
{
datatable1: { type: 'datatable', columns: [], rows: [] },
},
Expand All @@ -155,6 +238,8 @@ describe('getLayerMetaInfo', () => {
getLayerMetaInfo(
mockDatasource,
{},
createMockVisualization('testVisualization'),
{},
{
datatable1: { type: 'datatable', columns: [], rows: [] },
},
Expand All @@ -169,31 +254,26 @@ describe('getLayerMetaInfo', () => {
});

it('should basically work collecting fields and filters in the visualization', () => {
const mockDatasource = createMockDatasource('testDatasource');
const updatedPublicAPI: DatasourcePublicAPI = {
const mockDatasource = createMockDatasource('testDatasource', {
datasourceId: 'formBased',
getOperationForColumnId: jest.fn(),
getTableSpec: jest.fn(() => [{ columnId: 'col1', fields: ['bytes'] }]),
getVisualDefaults: jest.fn(),
getSourceId: jest.fn(() => '1'),
getMaxPossibleNumValues: jest.fn(),
isTextBasedLanguage: jest.fn(() => false),
getFilters: jest.fn(() => ({
enabled: {
kuery: [[{ language: 'kuery', query: 'memory > 40000' }]],
lucene: [],
},
disabled: { kuery: [], lucene: [] },
})),
hasDefaultTimeField: jest.fn(() => true),
};
mockDatasource.getPublicAPI.mockReturnValue(updatedPublicAPI);
});
const sampleIndexPatternsFromService = {
'1': createMockedIndexPattern(),
};
const { error, meta } = getLayerMetaInfo(
mockDatasource,
{}, // the publicAPI has been mocked, so no need for a state here
createMockVisualization('testVisualization'),
{},
{
datatable1: { type: 'datatable', columns: [], rows: [] },
},
Expand All @@ -220,31 +300,26 @@ describe('getLayerMetaInfo', () => {
});

it('should order date fields first', () => {
const mockDatasource = createMockDatasource('testDatasource');
const updatedPublicAPI: DatasourcePublicAPI = {
const mockDatasource = createMockDatasource('testDatasource', {
datasourceId: 'formBased',
getOperationForColumnId: jest.fn(),
getTableSpec: jest.fn(() => [{ columnId: 'col1', fields: ['bytes', 'timestamp'] }]),
getVisualDefaults: jest.fn(),
getSourceId: jest.fn(() => '1'),
getMaxPossibleNumValues: jest.fn(),
isTextBasedLanguage: jest.fn(() => false),
getFilters: jest.fn(() => ({
enabled: {
kuery: [[{ language: 'kuery', query: 'memory > 40000' }]],
lucene: [],
},
disabled: { kuery: [], lucene: [] },
})),
hasDefaultTimeField: jest.fn(() => true),
};
mockDatasource.getPublicAPI.mockReturnValue(updatedPublicAPI);
});
const sampleIndexPatternsFromService = {
'1': createMockedIndexPattern(),
};
const { meta } = getLayerMetaInfo(
mockDatasource,
{}, // the publicAPI has been mocked, so no need for a state here
createMockVisualization('testVisualization'),
{},
{
datatable1: { type: 'datatable', columns: [], rows: [] },
},
Expand Down