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] Enables ad-hoc dataviews #138732

Merged
merged 15 commits into from
Aug 29, 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.
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 @@ -26,7 +26,7 @@ describe('filter manager persistable state tests', () => {
const updatedFilters = inject(filters, [
{ type: DATA_VIEW_SAVED_OBJECT_TYPE, name: 'test123', id: '123' },
]);
expect(updatedFilters[0]).toHaveProperty('meta.index', undefined);
expect(updatedFilters[0]).toHaveProperty('meta.index', 'test');
});
});

Expand Down
3 changes: 2 additions & 1 deletion src/plugins/data/common/query/filters/persistable_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ export const inject = (filters: Filter[], references: SavedObjectReference[]) =>
...filter,
meta: {
...filter.meta,
index: reference && reference.id,
// if no reference has been found, keep the current "index" property (used for adhoc data views)
index: reference ? reference.id : filter.meta.index,
},
};
});
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/common/query/persistable_state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe('query service persistable state tests', () => {
const updatedQueryState = inject(queryState, [
{ type: DATA_VIEW_SAVED_OBJECT_TYPE, name: 'test123', id: '123' },
]);
expect(updatedQueryState.filters[0]).toHaveProperty('meta.index', undefined);
expect(updatedQueryState.filters[0]).toHaveProperty('meta.index', 'test');
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export const TextBasedLanguagesList = (props: TextBasedLanguagesListProps) => (
export function ChangeDataView({
isMissingCurrent,
currentDataViewId,
adHocDataViews,
onChangeDataView,
onAddField,
onDataViewCreated,
Expand Down Expand Up @@ -93,10 +94,21 @@ export function ChangeDataView({
useEffect(() => {
const fetchDataViews = async () => {
const dataViewsRefs = await data.dataViews.getIdsWithTitle();
if (adHocDataViews?.length) {
adHocDataViews.forEach((adHocDataView) => {
if (adHocDataView.id) {
dataViewsRefs.push({
title: adHocDataView.title,
name: adHocDataView.name,
id: adHocDataView.id,
});
}
});
}
setDataViewsList(dataViewsRefs);
};
fetchDataViews();
}, [data, currentDataViewId]);
}, [data, currentDataViewId, adHocDataViews]);

useEffect(() => {
if (trigger.label) {
Expand Down
7 changes: 7 additions & 0 deletions src/plugins/unified_search/public/dataview_picker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import React from 'react';
import type { EuiButtonProps, EuiSelectableProps } from '@elastic/eui';
import type { DataView } from '@kbn/data-views-plugin/public';
import type { AggregateQuery, Query } from '@kbn/es-query';
import { ChangeDataView } from './change_dataview';

Expand Down Expand Up @@ -44,6 +45,10 @@ export interface DataViewPickerProps {
* The id of the selected dataview.
*/
currentDataViewId?: string;
/**
* The adHocDataviews.
*/
adHocDataViews?: DataView[];
/**
* EuiSelectable properties.
*/
Expand Down Expand Up @@ -84,6 +89,7 @@ export interface DataViewPickerPropsExtended extends DataViewPickerProps {
export const DataViewPicker = ({
isMissingCurrent,
currentDataViewId,
adHocDataViews,
onChangeDataView,
onAddField,
onDataViewCreated,
Expand All @@ -102,6 +108,7 @@ export const DataViewPicker = ({
onAddField={onAddField}
onDataViewCreated={onDataViewCreated}
trigger={trigger}
adHocDataViews={adHocDataViews}
selectableProps={selectableProps}
textBasedLanguages={textBasedLanguages}
onSaveTextLanguageQuery={onSaveTextLanguageQuery}
Expand Down
25 changes: 25 additions & 0 deletions test/functional/page_objects/unified_search_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { FtrService } from '../ftr_provider_context';
export class UnifiedSearchPageObject extends FtrService {
private readonly retry = this.ctx.getService('retry');
private readonly testSubjects = this.ctx.getService('testSubjects');
private readonly find = this.ctx.getService('find');

public async switchDataView(switchButtonSelector: string, dataViewTitle: string) {
await this.testSubjects.click(switchButtonSelector);
Expand All @@ -35,4 +36,28 @@ export class UnifiedSearchPageObject extends FtrService {

return visibleText;
}

public async clickCreateNewDataView() {
await this.retry.waitForWithTimeout('data create new to be visible', 15000, async () => {
return await this.testSubjects.isDisplayed('dataview-create-new');
});
await this.testSubjects.click('dataview-create-new');
await this.retry.waitForWithTimeout(
'index pattern editor form to be visible',
15000,
async () => {
return await (await this.find.byClassName('indexPatternEditor__form')).isDisplayed();
}
);
await (await this.find.byClassName('indexPatternEditor__form')).click();
}

public async createNewDataView(dataViewName: string, adHoc?: boolean) {
await this.clickCreateNewDataView();
await this.testSubjects.setValue('createIndexPatternTitleInput', dataViewName, {
clearWithKeyboard: true,
typeCharByChar: true,
});
await this.testSubjects.click(adHoc ? 'exploreIndexPatternButton' : 'saveIndexPatternButton');
}
}
12 changes: 10 additions & 2 deletions x-pack/plugins/cases/server/common/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1033,7 +1033,11 @@ describe('common utils', () => {
].join('\n\n');

const extractedReferences = extractLensReferencesFromCommentString(
makeLensEmbeddableFactory(() => ({}), {}),
makeLensEmbeddableFactory(
() => ({}),
() => ({}),
{}
),
commentString
);

Expand Down Expand Up @@ -1132,7 +1136,11 @@ describe('common utils', () => {
].join('\n\n');

const updatedReferences = getOrUpdateLensReferences(
makeLensEmbeddableFactory(() => ({}), {}),
makeLensEmbeddableFactory(
() => ({}),
() => ({}),
{}
),
newCommentString,
{
references: currentCommentReferences,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,11 @@ describe('comments migrations', () => {
it('should remove time zone param from date histogram', () => {
const migrations = createCommentsMigrations({
persistableStateAttachmentTypeRegistry: new PersistableStateAttachmentTypeRegistry(),
lensEmbeddableFactory: makeLensEmbeddableFactory(() => ({}), {}),
lensEmbeddableFactory: makeLensEmbeddableFactory(
() => ({}),
() => ({}),
{}
),
});

expect(migrations['7.14.0']).toBeDefined();
Expand Down Expand Up @@ -574,7 +578,11 @@ describe('comments migrations', () => {

const migrations = createCommentsMigrations({
persistableStateAttachmentTypeRegistry,
lensEmbeddableFactory: makeLensEmbeddableFactory(() => ({}), {}),
lensEmbeddableFactory: makeLensEmbeddableFactory(
() => ({}),
() => ({}),
{}
),
});

it('migrates a persistable state attachment correctly', () => {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 23 additions & 10 deletions x-pack/plugins/lens/public/app_plugin/app.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ describe('Lens App', () => {

it('updates global filters with store state', async () => {
const services = makeDefaultServicesForApp();
const indexPattern = { id: 'index1' } as unknown as DataView;
const indexPattern = { id: 'index1', isPersisted: () => true } as unknown as DataView;
const pinnedField = { name: 'pinnedField' } as unknown as FieldSpec;
const pinnedFilter = buildExistsFilter(pinnedField, indexPattern);
services.data.query.filterManager.getFilters = jest.fn().mockImplementation(() => {
Expand Down Expand Up @@ -348,7 +348,9 @@ describe('Lens App', () => {
const customServices = makeDefaultServicesForApp();
customServices.dataViews.get = jest
.fn()
.mockImplementation((id) => Promise.resolve({ id, isTimeBased: () => true } as DataView));
.mockImplementation((id) =>
Promise.resolve({ id, isTimeBased: () => true, isPersisted: () => true } as DataView)
);
const { services } = await mountWith({ services: customServices });
expect(services.navigation.ui.TopNavMenu).toHaveBeenCalledWith(
expect.objectContaining({ showDatePicker: true }),
Expand All @@ -359,7 +361,9 @@ describe('Lens App', () => {
const customServices = makeDefaultServicesForApp();
customServices.dataViews.get = jest
.fn()
.mockImplementation((id) => Promise.resolve({ id, isTimeBased: () => true } as DataView));
.mockImplementation((id) =>
Promise.resolve({ id, isTimeBased: () => true, isPersisted: () => true } as DataView)
);
const customProps = makeDefaultProps();
customProps.datasourceMap.testDatasource.isTimeBased = () => true;
const { services } = await mountWith({ props: customProps, services: customServices });
Expand All @@ -372,7 +376,9 @@ describe('Lens App', () => {
const customServices = makeDefaultServicesForApp();
customServices.dataViews.get = jest
.fn()
.mockImplementation((id) => Promise.resolve({ id, isTimeBased: () => true } as DataView));
.mockImplementation((id) =>
Promise.resolve({ id, isTimeBased: () => true, isPersisted: () => true } as DataView)
);
const customProps = makeDefaultProps();
customProps.datasourceMap.testDatasource.isTimeBased = () => false;
const { services } = await mountWith({ props: customProps, services: customServices });
Expand Down Expand Up @@ -477,7 +483,14 @@ describe('Lens App', () => {
expect(services.navigation.ui.TopNavMenu).toHaveBeenCalledWith(
expect.objectContaining({
query: 'fake query',
indexPatterns: [{ id: 'mockip', isTimeBased: expect.any(Function), fields: [] }],
indexPatterns: [
{
id: 'mockip',
isTimeBased: expect.any(Function),
fields: [],
isPersisted: expect.any(Function),
},
],
}),
{}
);
Expand Down Expand Up @@ -823,7 +836,7 @@ describe('Lens App', () => {
});

it('saves app filters and does not save pinned filters', async () => {
const indexPattern = { id: 'index1' } as unknown as DataView;
const indexPattern = { id: 'index1', isPersisted: () => true } as unknown as DataView;
const field = { name: 'myfield' } as unknown as FieldSpec;
const pinnedField = { name: 'pinnedField' } as unknown as FieldSpec;
const unpinned = buildExistsFilter(field, indexPattern);
Expand Down Expand Up @@ -1032,7 +1045,7 @@ describe('Lens App', () => {

it('updates the filters when the user changes them', async () => {
const { instance, services, lensStore } = await mountWith({});
const indexPattern = { id: 'index1' } as unknown as DataView;
const indexPattern = { id: 'index1', isPersisted: () => true } as unknown as DataView;
const field = { name: 'myfield' } as unknown as FieldSpec;
expect(lensStore.getState()).toEqual({
lens: expect.objectContaining({
Expand Down Expand Up @@ -1085,7 +1098,7 @@ describe('Lens App', () => {
searchSessionId: `sessionId-3`,
}),
});
const indexPattern = { id: 'index1' } as unknown as DataView;
const indexPattern = { id: 'index1', isPersisted: () => true } as unknown as DataView;
const field = { name: 'myfield' } as unknown as FieldSpec;
act(() =>
services.data.query.filterManager.setFilters([buildExistsFilter(field, indexPattern)])
Expand Down Expand Up @@ -1218,7 +1231,7 @@ describe('Lens App', () => {
query: { query: 'new', language: 'lucene' },
})
);
const indexPattern = { id: 'index1' } as unknown as DataView;
const indexPattern = { id: 'index1', isPersisted: () => true } as unknown as DataView;
const field = { name: 'myfield' } as unknown as FieldSpec;
const pinnedField = { name: 'pinnedField' } as unknown as FieldSpec;
const unpinned = buildExistsFilter(field, indexPattern);
Expand Down Expand Up @@ -1275,7 +1288,7 @@ describe('Lens App', () => {
query: { query: 'new', language: 'lucene' },
})
);
const indexPattern = { id: 'index1' } as unknown as DataView;
const indexPattern = { id: 'index1', isPersisted: () => true } as unknown as DataView;
const field = { name: 'myfield' } as unknown as FieldSpec;
const pinnedField = { name: 'pinnedField' } as unknown as FieldSpec;
const unpinned = buildExistsFilter(field, indexPattern);
Expand Down
8 changes: 8 additions & 0 deletions x-pack/plugins/lens/public/app_plugin/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { LensInspector } from '../lens_inspector_service';
import { getEditPath } from '../../common';
import { isLensEqual } from './lens_document_equality';
import { IndexPatternServiceAPI, createIndexPatternService } from '../indexpattern_service/service';
import { replaceIndexpattern } from '../state_management/lens_slice';

export type SaveProps = Omit<OnSaveProps, 'onTitleDuplicate' | 'newDescription'> & {
returnToOrigin: boolean;
Expand Down Expand Up @@ -368,13 +369,20 @@ export function App({
createIndexPatternService({
dataViews: lensAppServices.dataViews,
uiSettings: lensAppServices.uiSettings,
uiActions: lensAppServices.uiActions,
core: { http, notifications },
updateIndexPatterns: (newIndexPatternsState, options) => {
dispatch(updateIndexPatterns(newIndexPatternsState));
if (options?.applyImmediately) {
dispatch(applyChanges());
}
},
replaceIndexPattern: (newIndexPattern, oldId, options) => {
dispatch(replaceIndexpattern({ newIndexPattern, oldId }));
if (options?.applyImmediately) {
dispatch(applyChanges());
}
},
}),
[dispatch, http, notifications, lensAppServices]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ export const isLensEqual = (
.map((type) =>
datasourceMap[type].isEqual(
doc1.state.datasourceStates[type],
doc1.references,
[...doc1.references, ...(doc1.state.internalReferences || [])],
doc2.state.datasourceStates[type],
doc2.references
[...doc2.references, ...(doc2.state.internalReferences || [])]
)
)
.every((res) => res);
Expand Down
Loading