Skip to content

Commit

Permalink
[lens] Index pattern suggest on drop
Browse files Browse the repository at this point in the history
  • Loading branch information
wylieconlon committed Jun 12, 2019
1 parent 9ea8b9a commit 604c6ed
Show file tree
Hide file tree
Showing 7 changed files with 428 additions and 19 deletions.
Expand Up @@ -21,7 +21,7 @@ import { ReactWrapper } from 'enzyme';
const waitForPromises = () => new Promise(resolve => setTimeout(resolve));

describe('workspace_panel', () => {
let mockVisualization: Visualization;
let mockVisualization: jest.Mocked<Visualization>;
let mockDatasource: DatasourceMock;

let expressionRendererMock: jest.Mock<React.ReactElement, [ExpressionRendererProps]>;
Expand Down Expand Up @@ -274,4 +274,136 @@ Object {
expect(instance.find(expressionRendererMock).length).toBe(1);
});
});

describe('suggestions from dropping in workspace panel', () => {
let mockDispatch: jest.Mock;

beforeEach(() => {
mockDispatch = jest.fn();
instance = mount(
<WorkspacePanel
activeDatasource={mockDatasource}
datasourceState={{}}
activeVisualizationId={null}
visualizationMap={{
vis: mockVisualization,
}}
visualizationState={{}}
datasourcePublicAPI={mockDatasource.publicAPIMock}
dispatch={mockDispatch}
ExpressionRenderer={expressionRendererMock}
/>
);
});

it('should immediately transition if exactly one suggestion is returned', () => {
const expectedTable = {
datasourceSuggestionId: 0,
isMultiRow: true,
columns: [],
};
mockDatasource.getDatasourceSuggestionsForField.mockReturnValueOnce([
{
state: {},
table: expectedTable,
},
]);
mockVisualization.getSuggestions.mockReturnValueOnce([
{
score: 0.5,
title: 'my title',
state: {},
datasourceSuggestionId: 0,
},
]);

instance.childAt(0).prop('onDrop')({
name: '@timestamp',
type: 'date',
searchable: false,
aggregatable: false,
});

expect(mockDatasource.getDatasourceSuggestionsForField).toHaveBeenCalledTimes(1);
expect(mockVisualization.getSuggestions).toHaveBeenCalledWith(
expect.objectContaining({
tables: [expectedTable],
})
);
expect(mockDispatch).toHaveBeenCalledWith({
type: 'SWITCH_VISUALIZATION',
newVisualizationId: 'vis',
initialState: {},
datasourceState: {},
});
});

it('should immediately transition to the first suggestion if there are multiple', () => {
mockDatasource.getDatasourceSuggestionsForField.mockReturnValueOnce([
{
state: {},
table: {
datasourceSuggestionId: 0,
isMultiRow: true,
columns: [],
},
},
{
state: {},
table: {
datasourceSuggestionId: 1,
isMultiRow: true,
columns: [],
},
},
]);
mockVisualization.getSuggestions.mockReturnValueOnce([
{
score: 0.8,
title: 'first suggestion',
state: {
isFirst: true,
},
datasourceSuggestionId: 1,
},
{
score: 0.5,
title: 'second suggestion',
state: {},
datasourceSuggestionId: 0,
},
]);

instance.childAt(0).prop('onDrop')({
name: '@timestamp',
type: 'date',
searchable: false,
aggregatable: false,
});

expect(mockDispatch).toHaveBeenCalledWith({
type: 'SWITCH_VISUALIZATION',
newVisualizationId: 'vis',
initialState: {
isFirst: true,
},
datasourceState: {},
});
});

it("should do nothing when the visualization can't use the suggestions", () => {
mockDatasource.getDatasourceSuggestionsForField.mockReturnValueOnce([]);

instance.childAt(0).prop('onDrop')({
name: '@timestamp',
type: 'date',
searchable: false,
aggregatable: false,
});

expect(mockDatasource.getDatasourceSuggestionsForField).toHaveBeenCalledTimes(1);
expect(mockVisualization.getSuggestions).toHaveBeenCalledTimes(1);
expect(mockDispatch).not.toHaveBeenCalled();
});
});
});
Expand Up @@ -37,9 +37,10 @@ export function WorkspacePanel({
dispatch,
ExpressionRenderer: ExpressionRendererComponent,
}: WorkspacePanelProps) {
function onDrop() {
function onDrop(item: unknown) {
const datasourceSuggestions = activeDatasource.getDatasourceSuggestionsForField(
datasourceState
datasourceState,
item
);

const suggestions = getSuggestions(
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/lens/public/editor_frame_plugin/mocks.tsx
Expand Up @@ -35,7 +35,7 @@ export function createMockDatasource(): DatasourceMock {
};

return {
getDatasourceSuggestionsForField: jest.fn(_state => []),
getDatasourceSuggestionsForField: jest.fn((_state, item) => []),
getDatasourceSuggestionsFromCurrentState: jest.fn(_state => []),
getPersistableState: jest.fn(),
getPublicAPI: jest.fn((_state, _setState) => publicAPIMock),
Expand Down
169 changes: 169 additions & 0 deletions x-pack/plugins/lens/public/indexpattern_plugin/indexpattern.test.tsx
Expand Up @@ -279,6 +279,175 @@ describe('IndexPattern Data Source', () => {
});
});

describe('#getDatasourceSuggestionsForField', () => {
describe('with no previous selections', () => {
let initialState: IndexPatternPrivateState;

beforeEach(async () => {
initialState = await indexPatternDatasource.initialize({
currentIndexPatternId: '1',
columnOrder: [],
columns: {},
});
});

it('should apply a bucketed aggregation for a string field', () => {
const suggestions = indexPatternDatasource.getDatasourceSuggestionsForField(initialState, {
name: 'source',
type: 'string',
aggregatable: true,
searchable: true,
});

expect(suggestions).toHaveLength(1);
expect(suggestions[0].state).toEqual(
expect.objectContaining({
columnOrder: ['col1', 'col2'],
columns: {
col1: expect.objectContaining({
operationType: 'terms',
sourceField: 'source',
}),
col2: expect.objectContaining({
operationType: 'count',
sourceField: 'documents',
}),
},
})
);
expect(suggestions[0].table).toEqual({
datasourceSuggestionId: 0,
isMultiRow: true,
columns: [
expect.objectContaining({
columnId: 'col1',
}),
expect.objectContaining({
columnId: 'col2',
}),
],
});
});

it('should apply a bucketed aggregation for a date field', () => {
const suggestions = indexPatternDatasource.getDatasourceSuggestionsForField(initialState, {
name: 'timestamp',
type: 'date',
aggregatable: true,
searchable: true,
});

expect(suggestions).toHaveLength(1);
expect(suggestions[0].state).toEqual(
expect.objectContaining({
columnOrder: ['col1', 'col2'],
columns: {
col1: expect.objectContaining({
operationType: 'date_histogram',
sourceField: 'timestamp',
}),
col2: expect.objectContaining({
operationType: 'count',
sourceField: 'documents',
}),
},
})
);
expect(suggestions[0].table).toEqual({
datasourceSuggestionId: 0,
isMultiRow: true,
columns: [
expect.objectContaining({
columnId: 'col1',
}),
expect.objectContaining({
columnId: 'col2',
}),
],
});
});

it('should select a metric for a number field', () => {
const suggestions = indexPatternDatasource.getDatasourceSuggestionsForField(initialState, {
name: 'bytes',
type: 'number',
aggregatable: true,
searchable: true,
});

expect(suggestions).toHaveLength(1);
expect(suggestions[0].state).toEqual(
expect.objectContaining({
columnOrder: ['col1', 'col2'],
columns: {
col1: expect.objectContaining({
sourceField: 'timestamp',
operationType: 'date_histogram',
}),
col2: expect.objectContaining({
sourceField: 'bytes',
operationType: 'sum',
}),
},
})
);
expect(suggestions[0].table).toEqual({
datasourceSuggestionId: 0,
isMultiRow: true,
columns: [
expect.objectContaining({
columnId: 'col1',
}),
expect.objectContaining({
columnId: 'col2',
}),
],
});
});
});

describe('with a prior column', () => {
let initialState: IndexPatternPrivateState;

beforeEach(async () => {
initialState = await indexPatternDatasource.initialize(persistedState);
});

it('should not suggest for string', () => {
expect(
indexPatternDatasource.getDatasourceSuggestionsForField(initialState, {
name: 'source',
type: 'string',
aggregatable: true,
searchable: true,
})
).toHaveLength(0);
});

it('should not suggest for date', () => {
expect(
indexPatternDatasource.getDatasourceSuggestionsForField(initialState, {
name: 'timestamp',
type: 'date',
aggregatable: true,
searchable: true,
})
).toHaveLength(0);
});

it('should not suggest for number', () => {
expect(
indexPatternDatasource.getDatasourceSuggestionsForField(initialState, {
name: 'bytes',
type: 'number',
aggregatable: true,
searchable: true,
})
).toHaveLength(0);
});
});
});

describe('#getPublicAPI', () => {
let publicAPI: DatasourcePublicAPI;

Expand Down

0 comments on commit 604c6ed

Please sign in to comment.